From db491cbb09ab3c6aa49074650961f553d9881752 Mon Sep 17 00:00:00 2001 From: mshwed Date: Thu, 28 Mar 2019 11:46:04 -0400 Subject: [PATCH] Added initial hashing for Streebog GOST R 34.11-94 --- src/core/config/Categories.json | 7 +- src/core/operations/Streebog.mjs | 54 + src/core/vendor/Streebog.mjs | 1318 ++++++++++++++ src/core/vendor/streebog/gostCipher.js | 2267 ++++++++++++++++++++++++ src/core/vendor/streebog/gostCoding.js | 1171 ++++++++++++ src/core/vendor/streebog/gostCrypto.js | 1657 +++++++++++++++++ src/core/vendor/streebog/gostDigest.js | 1269 +++++++++++++ src/core/vendor/streebog/gostRandom.js | 139 ++ 8 files changed, 7879 insertions(+), 3 deletions(-) create mode 100644 src/core/operations/Streebog.mjs create mode 100644 src/core/vendor/Streebog.mjs create mode 100644 src/core/vendor/streebog/gostCipher.js create mode 100644 src/core/vendor/streebog/gostCoding.js create mode 100644 src/core/vendor/streebog/gostCrypto.js create mode 100644 src/core/vendor/streebog/gostDigest.js create mode 100644 src/core/vendor/streebog/gostRandom.js diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 2fe8e8f7..bf1a6050 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -106,7 +106,8 @@ "Enigma", "Bombe", "Multiple Bombe", - "Typex" + "Typex", + "Streebog" ] }, { @@ -194,8 +195,8 @@ "Remove null bytes", "To Upper case", "To Lower case", - "To Case Insensitive Regex", - "From Case Insensitive Regex", + "To Case Insensitive Regex", + "From Case Insensitive Regex", "Add line numbers", "Remove line numbers", "To Table", diff --git a/src/core/operations/Streebog.mjs b/src/core/operations/Streebog.mjs new file mode 100644 index 00000000..0c46f4f7 --- /dev/null +++ b/src/core/operations/Streebog.mjs @@ -0,0 +1,54 @@ +/** + * @author mshwed [m@ttshwed.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import * as GostDigest from "../vendor/streebog/gostDigest"; +import * as GostCoding from "../vendor/streebog/gostCoding"; + +/** + * Streebog operation + */ +class Streebog extends Operation { + + /** + * Streebog constructor + */ + constructor() { + super(); + + this.name = "Streebog"; + this.module = "Crypto"; + this.description = "Streebog is a cryptographic hash function defined in the Russian national standard GOST R 34.11-2012 Information Technology \u2013 Cryptographic Information Security \u2013 Hash Function. It was created to replace an obsolete GOST hash function defined in the old standard GOST R 34.11-94, and as an asymmetric reply to SHA-3 competition by the US National Institute of Standards and Technology."; + this.infoURL = "https://en.wikipedia.org/wiki/Streebog"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + try { + const gostDigest = new GostDigest({name: 'GOST R 34.11', version: 1994}); + const gostCoding = new GostCoding(); + + const decode = gostCoding.Chars.decode(input, 'utf8'); + let hexEncode = gostCoding.Hex.encode(gostDigest.digest(decode)) + + return hexEncode.replace(/[^\-A-Fa-f0-9]/g, '').toLowerCase();; + } catch (err) { + console.log(err) + throw new OperationError("Test"); + } + } + +} + +export default Streebog; diff --git a/src/core/vendor/Streebog.mjs b/src/core/vendor/Streebog.mjs new file mode 100644 index 00000000..9b078b2d --- /dev/null +++ b/src/core/vendor/Streebog.mjs @@ -0,0 +1,1318 @@ +/*------------------------------------------------------------------------------------------------------------------------- + Created by Rudolf Nickolaev (https://github.com/rudonick/crypto/) + & 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. + * + */ + +(function(root, factory) { + /* + * Module imports and exports + * + */ // + if (typeof define === "function" && define.amd) { + define(["gostRandom", "gostCipher"], factory); + } else if (typeof exports === "object") { + module.exports = factory(require("gostRandom"), require("gostCipher")); + } else { + root.GostDigest = factory(root.GostRandom, root.GostCipher); + } + // +})(this, function(GostRandom, GostCipher) { + /* + * GOST R 34.11 + * Common methods + * + */ // + + var root = this; + var rootCrypto = root.crypto || root.msCrypto; + + var DataError = root.DataError || Error, + NotSupportedError = root.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 && 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"); + } // + + /** + * Algorithm name GOST R 34.11 or GOST R 34.11-12

+ * + * http://tools.ietf.org/html/rfc6986 + * + * The digest method returns digest data in according to GOST R 4311-2012.
+ * Size of digest also defines in algorithm name. + * + * + * @memberOf GostDigest + * @method digest + * @instance + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {ArrayBuffer} Digest of data + */ + var digest2012 = (function() // + { + // 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; + }; + })(); // + + /** + * Algorithm name GOST R 34.11-94

+ * + * 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() // + { + 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; + }; + })(); // + + /** + * Algorithm name SHA-1

+ * + * https://tools.ietf.org/html/rfc3174 + * + * The digest method returns digest data in according to SHA-1.
+ * + * @memberOf GostDigest + * @method digest + * @instance + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {ArrayBuffer} Digest of data + */ + var digestSHA1 = (function() // + { + // 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; + }; + })(); // + + /** + * Algorithm name GOST R 34.11-HMAC

+ * + * 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) { + // + // 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); + } // + + /** + * Algorithm name GOST R 34.11-HMAC

+ * + * 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) { + // + 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; + } // + + /** + * Algorithm name GOST R 34.11-KDF

+ * + * Simple generate key 256/512 bit random seed for derivation algorithms + * + * @memberOf GostDigest + * @method generateKey + * @instance + * @returns {ArrayBuffer} Generated key + */ + function generateKey() { + // + return getSeed(this.bitLength).buffer; + } // + + /** + * Algorithm name GOST R 34.11-PFXKDF

+ * + * Derive bits from password (PKCS12 mode) + * + * @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) { + // + 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; + } // + + /** + * Algorithm name GOST R 34.11-KDF

+ * + * 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) { + // + 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; + } // + + /** + * Algorithm name GOST R 34.11-PBKDF1

+ * + * Derive bits from password + * + * @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) { + // + 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; + } // + + /** + * Algorithm name GOST R 34.11-PBKDF2

+ * + * Derive bits from password + * + * @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) { + // + 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; + } // + + /** + * Algorithm name GOST R 34.11-CPKDF

+ * + * Derive bits from password. CryptoPro algorithm + * + * @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

+ * + * Derive key from derive bits subset + * + * @memberOf GostDigest + * @method deriveKey + * @instance + * @param {ArrayBuffer} baseKey + * @returns {ArrayBuffer} + */ + function deriveKey(baseKey) { + // + return this.deriveBits(baseKey, this.keySize * 8); + } // + + /** + * GOST R 34.11 Algorithm

+ * + * References: {@link http://tools.ietf.org/html/rfc6986} and {@link http://tools.ietf.org/html/rfc5831}

+ * + * Normalized algorithm identifier common parameters: + * + * + * + * Supported algorithms, modes and parameters: + * + * + * + * @class GostDigest + * @param {AlgorithmIdentifier} algorithm WebCryptoAPI algorithm identifier + */ + function GostDigest(algorithm) { + // + 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" + ); + } + } // + + return GostDigest; +}); diff --git a/src/core/vendor/streebog/gostCipher.js b/src/core/vendor/streebog/gostCipher.js new file mode 100644 index 00000000..b3ec0116 --- /dev/null +++ b/src/core/vendor/streebog/gostCipher.js @@ -0,0 +1,2267 @@ +/** + * @file GOST 28147-89/GOST R 34.12-2015/GOST R 32.13-2015 Encryption Algorithm + * @version 1.76 + * @copyright 2014-2016, Rudolf Nickolaev. All rights reserved. + */ + +/* + * 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. + * + * 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. + * + */ + +(function (root, factory) { + + /* + * Module imports and exports + * + */ // + module.exports = factory(require('./gostRandom')); + +// + +}(this, function (GostRandom) { + + /* + * Initial parameters and common algortithms of GOST 28147-89 + * + * http://tools.ietf.org/html/rfc5830 + * + */ // + + var root = this; + var rootCrypto = root.crypto || root.msCrypto; + var CryptoOperationData = root.ArrayBuffer; + var SyntaxError = root.SyntaxError || root.Error, + DataError = root.DataError || root.Error, + NotSupportedError = root.NotSupportedError || root.Error; + /* + * Check supported + * This implementation support only Little Endian arhitecture + */ + + var littleEndian = (function () { + var buffer = new CryptoOperationData(2); + new DataView(buffer).setInt16(0, 256, true); + return new Int16Array(buffer)[0] === 256; + })(); + + // Default initial vector + var defaultIV = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + + // Predefined sBox collection + var sBoxes = { + 'E-TEST': [ + 0x4, 0x2, 0xF, 0x5, 0x9, 0x1, 0x0, 0x8, 0xE, 0x3, 0xB, 0xC, 0xD, 0x7, 0xA, 0x6, + 0xC, 0x9, 0xF, 0xE, 0x8, 0x1, 0x3, 0xA, 0x2, 0x7, 0x4, 0xD, 0x6, 0x0, 0xB, 0x5, + 0xD, 0x8, 0xE, 0xC, 0x7, 0x3, 0x9, 0xA, 0x1, 0x5, 0x2, 0x4, 0x6, 0xF, 0x0, 0xB, + 0xE, 0x9, 0xB, 0x2, 0x5, 0xF, 0x7, 0x1, 0x0, 0xD, 0xC, 0x6, 0xA, 0x4, 0x3, 0x8, + 0x3, 0xE, 0x5, 0x9, 0x6, 0x8, 0x0, 0xD, 0xA, 0xB, 0x7, 0xC, 0x2, 0x1, 0xF, 0x4, + 0x8, 0xF, 0x6, 0xB, 0x1, 0x9, 0xC, 0x5, 0xD, 0x3, 0x7, 0xA, 0x0, 0xE, 0x2, 0x4, + 0x9, 0xB, 0xC, 0x0, 0x3, 0x6, 0x7, 0x5, 0x4, 0x8, 0xE, 0xF, 0x1, 0xA, 0x2, 0xD, + 0xC, 0x6, 0x5, 0x2, 0xB, 0x0, 0x9, 0xD, 0x3, 0xE, 0x7, 0xA, 0xF, 0x4, 0x1, 0x8 + ], + 'E-A': [ + 0x9, 0x6, 0x3, 0x2, 0x8, 0xB, 0x1, 0x7, 0xA, 0x4, 0xE, 0xF, 0xC, 0x0, 0xD, 0x5, + 0x3, 0x7, 0xE, 0x9, 0x8, 0xA, 0xF, 0x0, 0x5, 0x2, 0x6, 0xC, 0xB, 0x4, 0xD, 0x1, + 0xE, 0x4, 0x6, 0x2, 0xB, 0x3, 0xD, 0x8, 0xC, 0xF, 0x5, 0xA, 0x0, 0x7, 0x1, 0x9, + 0xE, 0x7, 0xA, 0xC, 0xD, 0x1, 0x3, 0x9, 0x0, 0x2, 0xB, 0x4, 0xF, 0x8, 0x5, 0x6, + 0xB, 0x5, 0x1, 0x9, 0x8, 0xD, 0xF, 0x0, 0xE, 0x4, 0x2, 0x3, 0xC, 0x7, 0xA, 0x6, + 0x3, 0xA, 0xD, 0xC, 0x1, 0x2, 0x0, 0xB, 0x7, 0x5, 0x9, 0x4, 0x8, 0xF, 0xE, 0x6, + 0x1, 0xD, 0x2, 0x9, 0x7, 0xA, 0x6, 0x0, 0x8, 0xC, 0x4, 0x5, 0xF, 0x3, 0xB, 0xE, + 0xB, 0xA, 0xF, 0x5, 0x0, 0xC, 0xE, 0x8, 0x6, 0x2, 0x3, 0x9, 0x1, 0x7, 0xD, 0x4 + ], + 'E-B': [ + 0x8, 0x4, 0xB, 0x1, 0x3, 0x5, 0x0, 0x9, 0x2, 0xE, 0xA, 0xC, 0xD, 0x6, 0x7, 0xF, + 0x0, 0x1, 0x2, 0xA, 0x4, 0xD, 0x5, 0xC, 0x9, 0x7, 0x3, 0xF, 0xB, 0x8, 0x6, 0xE, + 0xE, 0xC, 0x0, 0xA, 0x9, 0x2, 0xD, 0xB, 0x7, 0x5, 0x8, 0xF, 0x3, 0x6, 0x1, 0x4, + 0x7, 0x5, 0x0, 0xD, 0xB, 0x6, 0x1, 0x2, 0x3, 0xA, 0xC, 0xF, 0x4, 0xE, 0x9, 0x8, + 0x2, 0x7, 0xC, 0xF, 0x9, 0x5, 0xA, 0xB, 0x1, 0x4, 0x0, 0xD, 0x6, 0x8, 0xE, 0x3, + 0x8, 0x3, 0x2, 0x6, 0x4, 0xD, 0xE, 0xB, 0xC, 0x1, 0x7, 0xF, 0xA, 0x0, 0x9, 0x5, + 0x5, 0x2, 0xA, 0xB, 0x9, 0x1, 0xC, 0x3, 0x7, 0x4, 0xD, 0x0, 0x6, 0xF, 0x8, 0xE, + 0x0, 0x4, 0xB, 0xE, 0x8, 0x3, 0x7, 0x1, 0xA, 0x2, 0x9, 0x6, 0xF, 0xD, 0x5, 0xC + ], + 'E-C': [ + 0x1, 0xB, 0xC, 0x2, 0x9, 0xD, 0x0, 0xF, 0x4, 0x5, 0x8, 0xE, 0xA, 0x7, 0x6, 0x3, + 0x0, 0x1, 0x7, 0xD, 0xB, 0x4, 0x5, 0x2, 0x8, 0xE, 0xF, 0xC, 0x9, 0xA, 0x6, 0x3, + 0x8, 0x2, 0x5, 0x0, 0x4, 0x9, 0xF, 0xA, 0x3, 0x7, 0xC, 0xD, 0x6, 0xE, 0x1, 0xB, + 0x3, 0x6, 0x0, 0x1, 0x5, 0xD, 0xA, 0x8, 0xB, 0x2, 0x9, 0x7, 0xE, 0xF, 0xC, 0x4, + 0x8, 0xD, 0xB, 0x0, 0x4, 0x5, 0x1, 0x2, 0x9, 0x3, 0xC, 0xE, 0x6, 0xF, 0xA, 0x7, + 0xC, 0x9, 0xB, 0x1, 0x8, 0xE, 0x2, 0x4, 0x7, 0x3, 0x6, 0x5, 0xA, 0x0, 0xF, 0xD, + 0xA, 0x9, 0x6, 0x8, 0xD, 0xE, 0x2, 0x0, 0xF, 0x3, 0x5, 0xB, 0x4, 0x1, 0xC, 0x7, + 0x7, 0x4, 0x0, 0x5, 0xA, 0x2, 0xF, 0xE, 0xC, 0x6, 0x1, 0xB, 0xD, 0x9, 0x3, 0x8 + ], + 'E-D': [ + 0xF, 0xC, 0x2, 0xA, 0x6, 0x4, 0x5, 0x0, 0x7, 0x9, 0xE, 0xD, 0x1, 0xB, 0x8, 0x3, + 0xB, 0x6, 0x3, 0x4, 0xC, 0xF, 0xE, 0x2, 0x7, 0xD, 0x8, 0x0, 0x5, 0xA, 0x9, 0x1, + 0x1, 0xC, 0xB, 0x0, 0xF, 0xE, 0x6, 0x5, 0xA, 0xD, 0x4, 0x8, 0x9, 0x3, 0x7, 0x2, + 0x1, 0x5, 0xE, 0xC, 0xA, 0x7, 0x0, 0xD, 0x6, 0x2, 0xB, 0x4, 0x9, 0x3, 0xF, 0x8, + 0x0, 0xC, 0x8, 0x9, 0xD, 0x2, 0xA, 0xB, 0x7, 0x3, 0x6, 0x5, 0x4, 0xE, 0xF, 0x1, + 0x8, 0x0, 0xF, 0x3, 0x2, 0x5, 0xE, 0xB, 0x1, 0xA, 0x4, 0x7, 0xC, 0x9, 0xD, 0x6, + 0x3, 0x0, 0x6, 0xF, 0x1, 0xE, 0x9, 0x2, 0xD, 0x8, 0xC, 0x4, 0xB, 0xA, 0x5, 0x7, + 0x1, 0xA, 0x6, 0x8, 0xF, 0xB, 0x0, 0x4, 0xC, 0x3, 0x5, 0x9, 0x7, 0xD, 0x2, 0xE + ], + 'E-SC': [ + 0x3, 0x6, 0x1, 0x0, 0x5, 0x7, 0xd, 0x9, 0x4, 0xb, 0x8, 0xc, 0xe, 0xf, 0x2, 0xa, + 0x7, 0x1, 0x5, 0x2, 0x8, 0xb, 0x9, 0xc, 0xd, 0x0, 0x3, 0xa, 0xf, 0xe, 0x4, 0x6, + 0xf, 0x1, 0x4, 0x6, 0xc, 0x8, 0x9, 0x2, 0xe, 0x3, 0x7, 0xa, 0xb, 0xd, 0x5, 0x0, + 0x3, 0x4, 0xf, 0xc, 0x5, 0x9, 0xe, 0x0, 0x6, 0x8, 0x7, 0xa, 0x1, 0xb, 0xd, 0x2, + 0x6, 0x9, 0x0, 0x7, 0xb, 0x8, 0x4, 0xc, 0x2, 0xe, 0xa, 0xf, 0x1, 0xd, 0x5, 0x3, + 0x6, 0x1, 0x2, 0xf, 0x0, 0xb, 0x9, 0xc, 0x7, 0xd, 0xa, 0x5, 0x8, 0x4, 0xe, 0x3, + 0x0, 0x2, 0xe, 0xc, 0x9, 0x1, 0x4, 0x7, 0x3, 0xf, 0x6, 0x8, 0xa, 0xd, 0xb, 0x5, + 0x5, 0x2, 0xb, 0x8, 0x4, 0xc, 0x7, 0x1, 0xa, 0x6, 0xe, 0x0, 0x9, 0x3, 0xd, 0xf + ], + 'E-Z': [// This is default S-box in according to draft of new standard + 0xc, 0x4, 0x6, 0x2, 0xa, 0x5, 0xb, 0x9, 0xe, 0x8, 0xd, 0x7, 0x0, 0x3, 0xf, 0x1, + 0x6, 0x8, 0x2, 0x3, 0x9, 0xa, 0x5, 0xc, 0x1, 0xe, 0x4, 0x7, 0xb, 0xd, 0x0, 0xf, + 0xb, 0x3, 0x5, 0x8, 0x2, 0xf, 0xa, 0xd, 0xe, 0x1, 0x7, 0x4, 0xc, 0x9, 0x6, 0x0, + 0xc, 0x8, 0x2, 0x1, 0xd, 0x4, 0xf, 0x6, 0x7, 0x0, 0xa, 0x5, 0x3, 0xe, 0x9, 0xb, + 0x7, 0xf, 0x5, 0xa, 0x8, 0x1, 0x6, 0xd, 0x0, 0x9, 0x3, 0xe, 0xb, 0x4, 0x2, 0xc, + 0x5, 0xd, 0xf, 0x6, 0x9, 0x2, 0xc, 0xa, 0xb, 0x7, 0x8, 0x1, 0x4, 0x3, 0xe, 0x0, + 0x8, 0xe, 0x2, 0x5, 0x6, 0x9, 0x1, 0xc, 0xf, 0x4, 0xb, 0x0, 0xd, 0xa, 0x3, 0x7, + 0x1, 0x7, 0xe, 0xd, 0x0, 0x5, 0x8, 0x3, 0x4, 0xf, 0xa, 0x6, 0x9, 0xc, 0xb, 0x2 + ], + //S-box for digest + 'D-TEST': [ + 0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3, + 0xE, 0xB, 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9, + 0x5, 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB, + 0x7, 0xD, 0xA, 0x1, 0x0, 0x8, 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3, + 0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2, + 0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, 0x8, 0x5, 0x9, 0xC, 0xF, 0xE, + 0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC, + 0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, 0x8, 0xC + ], + 'D-A': [ + 0xA, 0x4, 0x5, 0x6, 0x8, 0x1, 0x3, 0x7, 0xD, 0xC, 0xE, 0x0, 0x9, 0x2, 0xB, 0xF, + 0x5, 0xF, 0x4, 0x0, 0x2, 0xD, 0xB, 0x9, 0x1, 0x7, 0x6, 0x3, 0xC, 0xE, 0xA, 0x8, + 0x7, 0xF, 0xC, 0xE, 0x9, 0x4, 0x1, 0x0, 0x3, 0xB, 0x5, 0x2, 0x6, 0xA, 0x8, 0xD, + 0x4, 0xA, 0x7, 0xC, 0x0, 0xF, 0x2, 0x8, 0xE, 0x1, 0x6, 0x5, 0xD, 0xB, 0x9, 0x3, + 0x7, 0x6, 0x4, 0xB, 0x9, 0xC, 0x2, 0xA, 0x1, 0x8, 0x0, 0xE, 0xF, 0xD, 0x3, 0x5, + 0x7, 0x6, 0x2, 0x4, 0xD, 0x9, 0xF, 0x0, 0xA, 0x1, 0x5, 0xB, 0x8, 0xE, 0xC, 0x3, + 0xD, 0xE, 0x4, 0x1, 0x7, 0x0, 0x5, 0xA, 0x3, 0xC, 0x8, 0xF, 0x6, 0x2, 0x9, 0xB, + 0x1, 0x3, 0xA, 0x9, 0x5, 0xB, 0x4, 0xF, 0x8, 0x6, 0x7, 0xE, 0xD, 0x0, 0x2, 0xC + ], + 'D-SC': [ + 0xb, 0xd, 0x7, 0x0, 0x5, 0x4, 0x1, 0xf, 0x9, 0xe, 0x6, 0xa, 0x3, 0xc, 0x8, 0x2, + 0x1, 0x2, 0x7, 0x9, 0xd, 0xb, 0xf, 0x8, 0xe, 0xc, 0x4, 0x0, 0x5, 0x6, 0xa, 0x3, + 0x5, 0x1, 0xd, 0x3, 0xf, 0x6, 0xc, 0x7, 0x9, 0x8, 0xb, 0x2, 0x4, 0xe, 0x0, 0xa, + 0xd, 0x1, 0xb, 0x4, 0x9, 0xc, 0xe, 0x0, 0x7, 0x5, 0x8, 0xf, 0x6, 0x2, 0xa, 0x3, + 0x2, 0xd, 0xa, 0xf, 0x9, 0xb, 0x3, 0x7, 0x8, 0xc, 0x5, 0xe, 0x6, 0x0, 0x1, 0x4, + 0x0, 0x4, 0x6, 0xc, 0x5, 0x3, 0x8, 0xd, 0xa, 0xb, 0xf, 0x2, 0x1, 0x9, 0x7, 0xe, + 0x1, 0x3, 0xc, 0x8, 0xa, 0x6, 0xb, 0x0, 0x2, 0xe, 0x7, 0x9, 0xf, 0x4, 0x5, 0xd, + 0xa, 0xb, 0x6, 0x0, 0x1, 0x3, 0x4, 0x7, 0xe, 0xd, 0x5, 0xf, 0x8, 0x2, 0x9, 0xc + ] + }; + + var C = new Uint8Array([ + 0x69, 0x00, 0x72, 0x22, 0x64, 0xC9, 0x04, 0x23, + 0x8D, 0x3A, 0xDB, 0x96, 0x46, 0xE9, 0x2A, 0xC4, + 0x18, 0xFE, 0xAC, 0x94, 0x00, 0xED, 0x07, 0x12, + 0xC0, 0x86, 0xDC, 0xC2, 0xEF, 0x4C, 0xA9, 0x2B + ]); + + function signed(x) { + return x >= 0x80000000 ? x - 0x100000000 : x; + } + + function unsigned(x) { + return x < 0 ? x + 0x100000000 : x; + } + + // Set random values into Uint8Arry + // Random generator + function randomSeed(e) { + GostRandom = GostRandom || root.GostRandom; + var randomSource = GostRandom ? new (GostRandom || root.GostRandom) : rootCrypto; + if (randomSource.getRandomValues) + randomSource.getRandomValues(e); + else + throw new NotSupportedError('Random generator not found'); + } + + // Get buffer + function buffer(d) { + if (d instanceof CryptoOperationData) + return d; + else if (d && d.buffer && d.buffer instanceof CryptoOperationData) + 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('CryptoOperationData required'); + } + + // Get byte array + function byteArray(d) { + return new Uint8Array(buffer(d)); + } + + // Clone byte array + function cloneArray(d) { + return new Uint8Array(byteArray(d)); + } + + + // Get int32 array + function intArray(d) { + return new Int32Array(buffer(d)); + } + + // Swap bytes for version 2015 + function swap32(b) { + return ((b & 0xff) << 24) + | ((b & 0xff00) << 8) + | ((b >> 8) & 0xff00) + | ((b >> 24) & 0xff); + } + + // + + /* + * Initial parameters and common algortithms of GOST R 34.12-15 + * Algorithm "Kuznechik" 128bit + * + */ // + + // Default initial vector + var defaultIV128 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + // Mult table for R function + var multTable = (function () { + + // Multiply two numbers in the GF(2^8) finite field defined + // by the polynomial x^8 + x^7 + x^6 + x + 1 = 0 */ + function gmul(a, b) { + var p = 0, counter, carry; + for (counter = 0; counter < 8; counter++) { + if (b & 1) + p ^= a; + carry = a & 0x80; // detect if x^8 term is about to be generated + a = (a << 1) & 0xff; + if (carry) + a ^= 0xc3; // replace x^8 with x^7 + x^6 + x + 1 + b >>= 1; + } + return p & 0xff; + } + + // It is required only this values for R function + // 0 1 2 3 4 5 6 7 + var x = [1, 16, 32, 133, 148, 192, 194, 251]; + var m = []; + for (var i = 0; i < 8; i++) { + m[i] = []; + for (var j = 0; j < 256; j++) + m[i][j] = gmul(x[i], j); + } + return m; + })(); + + // 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1 + var kB = [4, 2, 3, 1, 6, 5, 0, 7, 0, 5, 6, 1, 3, 2, 4, 0]; + + // R - function + function funcR(d) { + var sum = 0; + for (var i = 0; i < 16; i++) + sum ^= multTable[kB[i]][d[i]]; + + for (var i = 16; i > 0; --i) + d[i] = d[i - 1]; + d[0] = sum; + } + + function funcReverseR(d) { + var tmp = d[0]; + for (var i = 0; i < 15; i++) + d[i] = d[i + 1]; + d[15] = tmp; + + var sum = 0; + for (i = 0; i < 16; i++) + sum ^= multTable[kB[i]][d[i]]; + d[15] = sum; + } + + // Nonlinear transformation + var kPi = [ + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, + 233, 119, 240, 219, 147, 46, 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, + 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, 139, 1, 142, 79, + 5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31, + 235, 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, + 181, 112, 14, 86, 8, 12, 118, 18, 191, 114, 19, 71, 156, 183, 93, 135, + 21, 161, 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, 178, 177, + 50, 117, 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87, + 223, 245, 36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, + 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74, + 167, 151, 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, + 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, + 7, 88, 179, 64, 134, 172, 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, + 225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97, + 32, 113, 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, + 89, 166, 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182 + ]; + + var kReversePi = (function () { + var m = []; + for (var i = 0, n = kPi.length; i < n; i++) + m[kPi[i]] = i; + return m; + })(); + + function funcS(d) { + for (var i = 0; i < 16; ++i) + d[i] = kPi[d[i]]; + } + + function funcReverseS(d) { + for (var i = 0; i < 16; ++i) + d[i] = kReversePi[d[i]]; + } + + function funcX(a, b) { + for (var i = 0; i < 16; ++i) + a[i] ^= b[i]; + } + + function funcL(d) { + for (var i = 0; i < 16; ++i) + funcR(d); + } + + function funcReverseL(d) { + for (var i = 0; i < 16; ++i) + funcReverseR(d); + } + + function funcLSX(a, b) { + funcX(a, b); + funcS(a); + funcL(a); + } + + function funcReverseLSX(a, b) { + funcX(a, b); + funcReverseL(a); + funcReverseS(a); + } + + function funcF(inputKey, inputKeySecond, iterationConst) { + var tmp = new Uint8Array(inputKey); + funcLSX(inputKey, iterationConst); + funcX(inputKey, inputKeySecond); + inputKeySecond.set(tmp); + } + + function funcC(number, d) { + for (var i = 0; i < 15; i++) + d[i] = 0; + d[15] = number; + funcL(d); + } + + // + + /** + * Key schedule for GOST R 34.12-15 128bits + * + * @memberOf GostCipher + * @private + * @instance + * @method keySchedule + * @param {type} k + * @returns {Uint8Array} + */ + function keySchedule128(k) // + { + var keys = new Uint8Array(160), c = new Uint8Array(16); + keys.set(byteArray(k)); + for (var j = 0; j < 4; j++) { + var j0 = 32 * j, j1 = 32 * (j + 1); + keys.set(new Uint8Array(keys.buffer, j0, 32), j1); + for (var i = 1; i < 9; i++) { + funcC(j * 8 + i, c); + funcF(new Uint8Array(keys.buffer, j1, 16), + new Uint8Array(keys.buffer, j1 + 16, 16), c); + } + } + return keys; + } // + + /** + * GOST R 34.12-15 128 bits encrypt/decrypt process + * + * @memberOf GostCipher + * @private + * @instance + * @method round + * @param {Uint8Array} k Scheduled key + * @param {Uint8Array} d Data + * @param {number} ofs Offsec + * @param {number} e true - decrypt + */ + function process128(k, d, ofs, e) // + { + ofs = ofs || d.byteOffset; + var r = new Uint8Array(d.buffer, ofs, 16); + if (e) { + for (var i = 0; i < 9; i++) + funcReverseLSX(r, new Uint8Array(k.buffer, (9 - i) * 16, 16)); + + funcX(r, new Uint8Array(k.buffer, 0, 16)); + } else { + for (var i = 0; i < 9; i++) + funcLSX(r, new Uint8Array(k.buffer, 16 * i, 16)); + + funcX(r, new Uint8Array(k.buffer, 16 * 9, 16)); + } + } // + + /** + * One GOST encryption round + * + * @memberOf GostCipher + * @private + * @instance + * @method round + * @param {Int8Array} S sBox + * @param {Int32Array} m 2x32 bits cipher block + * @param {Int32Array} k 32 bits key[i] + */ + function round(S, m, k) // + { + var cm = (m[0] + k) & 0xffffffff; + + var om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4); + om |= S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4); + om |= S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4); + om |= S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4); + om |= S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4); + om |= S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4); + om |= S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4); + om |= S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4); + cm = om << 11 | om >>> (32 - 11); + + cm ^= m[1]; + m[1] = m[0]; + m[0] = cm; + + } // + + /** + * Process encrypt/decrypt block with key K using GOST 28147-89 + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Int32Array} 8x32 bits key + * @param d {Int32Array} 8x8 bits cipher block + * @param ofs {number} offset + */ + function process89(k, d, ofs) // + { + ofs = ofs || d.byteOffset; + var s = this.sBox, + m = new Int32Array(d.buffer, ofs, 2); + + for (var i = 0; i < 32; i++) + round(s, m, k[i]); + + var r = m[0]; + m[0] = m[1]; + m[1] = r; + } // + + /** + * Process encrypt/decrypt block with key K using GOST R 34.12-15 64bit block + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Int32Array} 8x32 bits key + * @param d {Int32Array} 8x8 bits cipher block + * @param ofs {number} offset + */ + function process15(k, d, ofs) // + { + ofs = ofs || d.byteOffset; + var s = this.sBox, + m = new Int32Array(d.buffer, ofs, 2), + r = swap32(m[0]); + m[0] = swap32(m[1]); + m[1] = r; + + for (var i = 0; i < 32; i++) + round(s, m, k[i]); + + m[0] = swap32(m[0]); + m[1] = swap32(m[1]); + } // + + /** + * Key keySchedule algorithm for GOST 28147-89 64bit cipher + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Uint8Array} 8 bit key array + * @param e {boolean} true - decrypt + * @returns {Int32Array} keyScheduled 32-bit key + */ + function keySchedule89(k, e) // + { + var sch = new Int32Array(32), + key = new Int32Array(buffer(k)); + + for (var i = 0; i < 8; i++) + sch[i] = key[i]; + + if (e) { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[7 - i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[7 - i]; + } else { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[i]; + } + + for (var i = 0; i < 8; i++) + sch[i + 24] = sch[7 - i]; + + return sch; + } // + + /** + * Key keySchedule algorithm for GOST R 34.12-15 64bit cipher + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Uint8Array} 8 bit key array + * @param e {boolean} true - decrypt + * @returns {Int32Array} keyScheduled 32-bit key + */ + function keySchedule15(k, e) // + { + var sch = new Int32Array(32), + key = new Int32Array(buffer(k)); + + for (var i = 0; i < 8; i++) + sch[i] = swap32(key[i]); + + if (e) { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[7 - i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[7 - i]; + } else { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[i]; + } + + for (var i = 0; i < 8; i++) + sch[i + 24] = sch[7 - i]; + + return sch; + } // + + /** + * Key schedule for RC2 + * + * https://tools.ietf.org/html/rfc2268 + * + * @memberOf GostCipher + * @private + * @instance + * @method keySchedule + * @param {Uint8Array} k + * @returns {Uint16Array} + */ + var keyScheduleRC2 = (function () // + { + // an array of "random" bytes based on the digits of PI = 3.14159... + var PITABLE = new Uint8Array([ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad + ]); + + return function (k) + { + var key = new Uint8Array(buffer(k)), + T = Math.min(key.length, 128), + T1 = this.effectiveLength, + T8 = Math.floor((T1 + 7) / 8), + TM = 0xff % Math.pow(2, 8 + T1 - 8 * T8); + + var L = new Uint8Array(128), K = new Uint16Array(L.buffer); + for (var i = 0; i < T; i++) + L[i] = key[i]; + for (var i = T; i < 128; i++) + L[i] = PITABLE[(L[i - 1] + L[i - T]) % 256]; + L[128 - T8] = PITABLE[L[128 - T8] & TM]; + for (var i = 127 - T8; i >= 0; --i) + L[i] = PITABLE[L[i + 1] ^ L[i + T8]]; + return K; + }; + } // + )(); + + /** + * RC2 encrypt/decrypt process + * + * https://tools.ietf.org/html/rfc2268 + * + * @memberOf GostCipher + * @private + * @instance + * @method round + * @param {CryptoOperationData} k Scheduled key + * @param {CryptoOperationData} d Data + * @param {number} ofs Offsec + * @param {number} e true - decrypt + */ + var processRC2 = (function () // + { + var K, j, R = new Uint16Array(4), + s = new Uint16Array([1, 2, 3, 5]), reverse; + + function rol(R, s) { + return (R << s | R >>> (16 - s)) & 0xffff; + } + + function ror(R, s) { + return (R >>> s | R << (16 - s)) & 0xffff; + } + + function mix(i) { + if (reverse) { + R[i] = ror(R[i], s[i]); + R[i] = R[i] - K[j] - (R[(i + 3) % 4] & R[(i + 2) % 4]) - ((~R[(i + 3) % 4]) & R[(i + 1) % 4]); + j = j - 1; + } else { + R[i] = R[i] + K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) + ((~R[(i + 3) % 4]) & R[(i + 1) % 4]); + j = j + 1; + R[i] = rol(R[i], s[i]); + } + } + + function mash(i) { + if (reverse) { + R[i] = R[i] - K[R[(i + 3) % 4] & 63]; + } else { + R[i] = R[i] + K[R[(i + 3) % 4] & 63]; + } + } + + function perform(method, count) { + count = count || 1; + for (var j = 0; j < count; j++) { + if (reverse) { + for (var i = 3; i >= 0; --i) + method(i); + } else { + for (var i = 0; i < 4; i++) + method(i); + } + } + } + + return function (k, d, ofs, e) { + reverse = e; + // 1. Initialize words R[0], ..., R[3] to contain the 64-bit + // ciphertext value. + R = new Uint16Array(d.buffer, ofs || d.byteOffset, 4); + // 2. Expand the key, so that words K[0], ..., K[63] become + // defined. + K = k; + // 3. Initialize j to zero (enc) j to 63 (dec). + j = e ? 63 : 0; + // 4. Perform five mixing rounds. + perform(mix, 5); + // 5. Perform one mashing round. + perform(mash); + // 6. Perform six mixing rounds. + perform(mix, 6); + // 7. Perform one mashing round. + perform(mash); + // 8. Perform five mixing rounds. + perform(mix, 5); + }; + } // + )(); + + /** + * Algorithm name GOST 28147-ECB

+ * + * encryptECB (K, D) is D, encrypted with key k using GOST 28147/GOST R 34.13 in + * "prostaya zamena" (Electronic Codebook, ECB) mode. + * @memberOf GostCipher + * @method encrypt + * @instance + * @param k {CryptoOperationData} 8x32 bit key + * @param d {CryptoOperationData} 8 bits message + * @return {CryptoOperationData} result + */ + function encryptECB(k, d) // + { + var p = this.pad(byteArray(d)), + n = this.blockSize, + b = p.byteLength / n, + key = this.keySchedule(k); + + for (var i = 0; i < b; i++) + this.process(key, p, n * i); + + return p.buffer; + } // + + /** + * Algorithm name GOST 28147-ECB

+ * + * decryptECB (K, D) is D, decrypted with key K using GOST 28147/GOST R 34.13 in + * "prostaya zamena" (Electronic Codebook, ECB) mode. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param k {CryptoOperationData} 8x32 bits key + * @param d {CryptoOperationData} 8 bits message + * @return {CryptoOperationData} result + */ + function decryptECB(k, d) // + { + var p = cloneArray(d), + n = this.blockSize, + b = p.byteLength / n, + key = this.keySchedule(k, 1); + + for (var i = 0; i < b; i++) + this.process(key, p, n * i, 1); + + return this.unpad(p).buffer; + } // + + /** + * Algorithm name GOST 28147-CFB

+ * + * encryptCFB (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu" (Cipher Feedback, CFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ + function encryptCFB(k, d, iv) // + { + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + m = s.length, + t = new Uint8Array(m), + b = this.shiftBits >> 3, + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < m; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) + c[i * b + j] ^= s[j]; + + for (var j = 0; j < m - b; j++) + s[j] = t[b + j]; + + for (var j = 0; j < b; j++) + s[m - b + j] = c[i * b + j]; + + k = this.keyMeshing(k, s, i, key); + } + + if (r > 0) { + this.process(key, s); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; + } // + + /** + * Algorithm name GOST 28147-CFB

+ * + * decryptCFB (IV, K, D) is D, decrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu po shifrotekstu" (Cipher Feedback, CFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ + function decryptCFB(k, d, iv) // + { + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + m = s.length, + t = new Uint8Array(m), + b = this.shiftBits >> 3, + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < m; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) { + t[j] = c[i * b + j]; + c[i * b + j] ^= s[j]; + } + + for (var j = 0; j < m - b; j++) + s[j] = t[b + j]; + + for (var j = 0; j < b; j++) + s[m - b + j] = t[j]; + + k = this.keyMeshing(k, s, i, key); + } + + if (r > 0) { + this.process(key, s); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; + } // + + /** + * Algorithm name GOST 28147-OFB

+ * + * encryptOFB/decryptOFB (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu po vyhodu" (Output Feedback, OFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv 8x8 optional bits initial vector + * @return {CryptoOperationData} result + */ + /** + * Algorithm name GOST 28147-OFB

+ * + * encryptOFB/decryptOFB (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu po vyhodu" (Output Feedback, OFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ + function processOFB(k, d, iv) // + { + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + m = s.length, + t = new Uint8Array(m), + b = this.shiftBits >> 3, + p = new Uint8Array(b), + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < m; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) + p[j] = s[j]; + + for (var j = 0; j < b; j++) + c[i * b + j] ^= s[j]; + + for (var j = 0; j < m - b; j++) + s[j] = t[b + j]; + + for (var j = 0; j < b; j++) + s[m - b + j] = p[j]; + + k = this.keyMeshing(k, s, i, key); + } + + if (r > 0) { + this.process(key, s); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; + } // + + /** + * Algorithm name GOST 28147-CTR

+ * + * encryptCTR/decryptCTR (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie" (Counter Mode-CTR) mode, and IV is used as the + * initialization vector. + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv 8x8 optional bits initial vector + * @return {CryptoOperationData} result + */ + /** + * Algorithm name GOST 28147-CTR

+ * + * encryptCTR/decryptCTR (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie" (Counter Mode-CTR) mode, and IV is used as the + * initialization vector. + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ + function processCTR89(k, d, iv) // + { + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + b = this.blockSize, + t = new Int8Array(b), + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k), + syn = new Int32Array(s.buffer); + + this.process(key, s); + + for (var i = 0; i < q; i++) { + syn[0] = (syn[0] + 0x1010101) & 0xffffffff; + // syn[1] = signed(unsigned((syn[1] + 0x1010104) & 0xffffffff) % 0xffffffff); + var tmp = unsigned(syn[1]) + 0x1010104; // Special thanks to Ilya Matveychikov + syn[1] = signed(tmp < 0x100000000 ? tmp : tmp - 0xffffffff); + + for (var j = 0; j < b; j++) + t[j] = s[j]; + + this.process(key, syn); + + for (var j = 0; j < b; j++) + c[i * b + j] ^= s[j]; + + for (var j = 0; j < b; j++) + s[j] = t[j]; + + k = this.keyMeshing(k, s, i, key); + } + if (r > 0) { + syn[0] = (syn[0] + 0x1010101) & 0xffffffff; + // syn[1] = signed(unsigned((syn[1] + 0x1010104) & 0xffffffff) % 0xffffffff); + var tmp = unsigned(syn[1]) + 0x1010104; // Special thanks to Ilya Matveychikov + syn[1] = signed(tmp < 0x100000000 ? tmp : tmp - 0xffffffff); + + this.process(key, syn); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; + } // + + function processCTR15(k, d, iv) // + { + var c = cloneArray(d), + n = this.blockSize, + b = this.shiftBits >> 3, + cb = c.length, r = cb % b, q = (cb - r) / b, + s = new Uint8Array(n), + t = new Int32Array(n), + key = this.keySchedule(k); + + s.set(iv || this.iv); + for (var i = 0; i < q; i++) { + + for (var j = 0; j < n; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) + c[b * i + j] ^= s[j]; + + for (var j = 0; j < n; j++) + s[j] = t[j]; + + for (var j = n - 1; i >= 0; --i) { + if (s[j] > 0xfe) { + s[j] -= 0xfe; + } else { + s[j]++; + break; + } + } + } + + if (r > 0) { + this.process(key, s); + for (var j = 0; j < r; j++) + c[b * q + j] ^= s[j]; + } + + return c.buffer; + } // + + /** + * Algorithm name GOST 28147-CBC

+ * + * encryptCBC (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "Prostaya zamena s zatsepleniem" (Cipher-Block-Chaining, CBC) mode and IV is used as the initialization + * vector. + * + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ + function encryptCBC(k, d, iv) // + { + var s = new Uint8Array(iv || this.iv), + n = this.blockSize, + m = s.length, + c = this.pad(byteArray(d)), + key = this.keySchedule(k); + + for (var i = 0, b = c.length / n; i < b; i++) { + + for (var j = 0; j < n; j++) + s[j] ^= c[i * n + j]; + + this.process(key, s); + + for (var j = 0; j < n; j++) + c[i * n + j] = s[j]; + + if (m !== n) { + for (var j = 0; j < m - n; j++) + s[j] = s[n + j]; + + for (var j = 0; j < n; j++) + s[j + m - n] = c[i * n + j]; + } + + k = this.keyMeshing(k, s, i, key); + } + + return c.buffer; + } // + + /** + * Algorithm name GOST 28147-CBC

+ * + * decryptCBC (IV, K, D) is D, decrypted with key K using GOST 28147/GOST R 34.13 + * in "Prostaya zamena s zatsepleniem" (Cipher-Block-Chaining, CBC) mode and IV is used as the initialization + * vector. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ + function decryptCBC(k, d, iv) // + { + var s = new Uint8Array(iv || this.iv), + n = this.blockSize, + m = s.length, + c = cloneArray(d), + next = new Uint8Array(n), + key = this.keySchedule(k, 1); + + for (var i = 0, b = c.length / n; i < b; i++) { + + for (var j = 0; j < n; j++) + next[j] = c[i * n + j]; + + this.process(key, c, i * n, 1); + + for (var j = 0; j < n; j++) + c[i * n + j] ^= s[j]; + + if (m !== n) { + for (var j = 0; j < m - n; j++) + s[j] = s[n + j]; + } + + for (var j = 0; j < n; j++) + s[j + m - n] = next[j]; + + k = this.keyMeshing(k, s, i, key, 1); + } + + return this.unpad(c).buffer; + } // + + /** + * The generateKey method returns a new generated key. + * + * @memberOf GostCipher + * @method generateKey + * @instance + * @return {CryptoOperationData} result + */ + + function generateKey() // + { + // Simple generate 256 bit random seed + var k = new Uint8Array(this.keySize); + randomSeed(k); + return k.buffer; + } // + + + /** + * makeIMIT (K, D) is the 32-bit result of the GOST 28147/GOST R 34.13 in + * "imitovstavka" (MAC) mode, used with D as plaintext, K as key and IV + * as initialization vector. Note that the standard specifies its use + * in this mode only with an initialization vector of zero. + * + * @memberOf GostCipher + * @method processMAC + * @private + * @instance + * @param {Int32Array} key 8x32 bits key + * @param {Int32Array} s 8x8 sum array + * @param {Uint8Array} d 8 bits array with data + * @return {Uint8Array} result + */ + function processMAC89(key, s, d) // + { + var c = zeroPad.call(this, byteArray(d)), + n = this.blockSize, + q = c.length / n, + sBox = this.sBox, + sum = new Int32Array(s.buffer); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < n; j++) + s[j] ^= c[i * n + j]; + + for (var j = 0; j < 16; j++) // 1-16 steps + round(sBox, sum, key[j]); + } + } // + + function processKeyMAC15(s) // + { + var t = 0, n = s.length; + for (var i = n - 1; i >= 0; --i) { + var t1 = s[i] >>> 7; + s[i] = (s[i] << 1) & 0xff | t; + t = t1; + } + if (t !== 0) { + if (n === 16) + s[15] ^= 0x87; + else + s[7] ^= 0x1b; + } + } // + + function processMAC15(key, s, d) // + { + var n = this.blockSize, + sBox = this.sBox, c = byteArray(d), + r = new Uint8Array(n); + // R + this.process(key, r); + // K1 + processKeyMAC15(r); + if (d.byteLength % n !== 0) { + c = bitPad.call(this, byteArray(d)); + // K2 + processKeyMAC15(r); + } + + for (var i = 0, q = c.length / n; i < q; i++) { + + for (var j = 0; j < n; j++) + s[j] ^= c[i * n + j]; + + if (i === q - 1) {// Last block + for (var j = 0; j < n; j++) + s[j] ^= r[j]; + } + + this.process(key, s); + } + } // + + /** + * signMAC (K, D, IV) is the 32-bit result of the GOST 28147/GOST R 34.13 in + * "imitovstavka" (MAC) mode, used with D as plaintext, K as key and IV + * as initialization vector. Note that the standard specifies its use + * in this mode only with an initialization vector of zero. + * + * @memberOf GostCipher + * @method sign + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ + function signMAC(k, d, iv) // + { + var key = this.keySchedule(k), + s = new Uint8Array(iv || this.iv), + m = Math.ceil(this.macLength >> 3) || this.blockSize >> 1; + + this.processMAC(key, s, d); + + var mac = new Uint8Array(m); // mac size + mac.set(new Uint8Array(s.buffer, 0, m)); + return mac.buffer; + } // + + /** + * verifyMAC (K, M, D, IV) the 32-bit result verification of the GOST 28147/GOST R 34.13 in + * "imitovstavka" (MAC) mode, used with D as plaintext, K as key and IV + * as initialization vector. Note that the standard specifies its use + * in this mode only with an initialization vector of zero. + * + * @memberOf GostCipher + * @method verify + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} m 8 bits array with signature + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv 8x8 optional bits initial vector + * @return {boolen} MAC verified = true + */ + function verifyMAC(k, m, d, iv) // + { + var mac = new Uint8Array(signMAC.call(this, k, d, iv)), + test = byteArray(m); + if (mac.length !== test.length) + return false; + for (var i = 0, n = mac.length; i < n; i++) + if (mac[i] !== test[i]) + return false; + return true; + } // + + /** + * Algorithm name GOST 28147-KW

+ * + * This algorithm encrypts GOST 28147-89 CEK with a GOST 28147/GOST R 34.13 KEK. + * Ref. rfc4357 6.1 GOST 28147-89 Key Wrap + * Note: This algorithm MUST NOT be used with a KEK produced by VKO GOST + * R 34.10-94, because such a KEK is constant for every sender-recipient + * pair. Encrypting many different content encryption keys on the same + * constant KEK may reveal that KEK. + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} cek Content encryption key + * @returns {CryptoOperationData} Encrypted cek + */ + function wrapKeyGOST(kek, cek) // + { + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) For a unique symmetric KEK, generate 8 octets at random and call + // the result UKM. For a KEK, produced by VKO GOST R 34.10-2001, use + // the UKM that was used for key derivation. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm); + // 2) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK, CEK). + // Call the result CEK_MAC. + var mac = signMAC.call(this, kek, cek, ukm); + // 3) Encrypt the CEK in ECB mode using the KEK. Call the ciphertext CEK_ENC. + var enc = encryptECB.call(this, kek, cek); + // 4) The wrapped content-encryption key is (UKM | CEK_ENC | CEK_MAC). + var r = new Uint8Array(len); + r.set(new Uint8Array(enc), 0); + r.set(new Uint8Array(mac), k); + return r.buffer; + } // + + /** + * Algorithm name GOST 28147-KW

+ * + * This algorithm decrypts GOST 28147-89 CEK with a GOST 28147 KEK. + * Ref. rfc4357 6.2 GOST 28147-89 Key Unwrap + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {type} kek Key encryption key + * @param {type} data Content encryption key + * @return {CryptoOperationData} result + */ + function unwrapKeyGOST(kek, data) // + { + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) If the wrapped content-encryption key is not 44 octets, then error. + var d = buffer(data); + if (d.byteLength !== len) + throw new DataError('Wrapping key size must be ' + len + ' bytes'); + // 2) Decompose the wrapped content-encryption key into UKM, CEK_ENC, and CEK_MAC. + // UKM is the most significant (first) 8 octets. CEK_ENC is next 32 octets, + // and CEK_MAC is the least significant (last) 4 octets. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm), + enc = new Uint8Array(d, 0, k), + mac = new Uint8Array(d, k, n >> 1); + // 3) Decrypt CEK_ENC in ECB mode using the KEK. Call the output CEK. + var cek = decryptECB.call(this, kek, enc); + // 4) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK, CEK), + // compare the result with CEK_MAC. If they are not equal, then error. + var check = verifyMAC.call(this, kek, mac, cek, ukm); + if (!check) + throw new DataError('Error verify MAC of wrapping key'); + return cek; + } // + + /** + * Algorithm name GOST 28147-CPKW

+ * + * Given a random 64-bit UKM and a GOST 28147 key K, this algorithm + * creates a new GOST 28147-89 key K(UKM). + * Ref. rfc4357 6.3 CryptoPro KEK Diversification Algorithm + * + * @memberOf GostCipher + * @method diversify + * @instance + * @private + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} ukm Random generated value + * @returns {CryptoOperationData} Diversified kek + */ + function diversifyKEK(kek, ukm) // + { + var n = this.blockSize; + + // 1) Let K[0] = K; + var k = intArray(kek); + // 2) UKM is split into components a[i,j]: + // UKM = a[0]|..|a[7] (a[i] - byte, a[i,0]..a[i,7] - it’s bits) + var a = []; + for (var i = 0; i < n; i++) { + a[i] = []; + for (var j = 0; j < 8; j++) { + a[i][j] = (ukm[i] >>> j) & 0x1; + } + } + // 3) Let i be 0. + // 4) K[1]..K[8] are calculated by repeating the following algorithm + // eight times: + for (var i = 0; i < n; i++) { + // A) K[i] is split into components k[i,j]: + // K[i] = k[i,0]|k[i,1]|..|k[i,7] (k[i,j] - 32-bit integer) + // B) Vector S[i] is calculated: + // S[i] = ((a[i,0]*k[i,0] + ... + a[i,7]*k[i,7]) mod 2^32) | + // (((~a[i,0])*k[i,0] + ... + (~a[i,7])*k[i,7]) mod 2^32); + var s = new Int32Array(2); + for (var j = 0; j < 8; j++) { + if (a[i][j]) + s[0] = (s[0] + k[j]) & 0xffffffff; + else + s[1] = (s[1] + k[j]) & 0xffffffff; + } + // C) K[i+1] = encryptCFB (S[i], K[i], K[i]) + var iv = new Uint8Array(s.buffer); + k = new Int32Array(encryptCFB.call(this, k, k, iv)); + // D) i = i + 1 + } + // 5) Let K(UKM) be K[8]. + return k; + } // + + /** + * Algorithm name GOST 28147-CPKW

+ * + * This algorithm encrypts GOST 28147-89 CEK with a GOST 28147 KEK. + * It can be used with any KEK (e.g., produced by VKO GOST R 34.10-94 or + * VKO GOST R 34.10-2001) because a unique UKM is used to diversify the KEK. + * Ref. rfc4357 6.3 CryptoPro Key Wrap + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} cek Content encryption key + * @returns {CryptoOperationData} Encrypted cek + */ + function wrapKeyCP(kek, cek) // + { + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) For a unique symmetric KEK or a KEK produced by VKO GOST R + // 34.10-94, generate 8 octets at random. Call the result UKM. For + // a KEK, produced by VKO GOST R 34.10-2001, use the UKM that was + // used for key derivation. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm); + // 2) Diversify KEK, using the CryptoPro KEK Diversification Algorithm, + // described in Section 6.5. Call the result KEK(UKM). + var dek = diversifyKEK.call(this, kek, ukm); + // 3) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK(UKM), + // CEK). Call the result CEK_MAC. + var mac = signMAC.call(this, dek, cek, ukm); + // 4) Encrypt CEK in ECB mode using KEK(UKM). Call the ciphertext + // CEK_ENC. + var enc = encryptECB.call(this, dek, cek); + // 5) The wrapped content-encryption key is (UKM | CEK_ENC | CEK_MAC). + var r = new Uint8Array(len); + r.set(new Uint8Array(enc), 0); + r.set(new Uint8Array(mac), k); + return r.buffer; + } // + + /** + * Algorithm name GOST 28147-CPKW

+ * + * This algorithm encrypts GOST 28147-89 CEK with a GOST 28147 KEK. + * Ref. rfc4357 6.4 CryptoPro Key Unwrap + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} data Encrypted content encryption keu + * @return {CryptoOperationData} result Decrypted content encryption keu + */ + function unwrapKeyCP(kek, data) // + { + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) If the wrapped content-encryption key is not 44 octets, then error. + var d = buffer(data); + if (d.byteLength !== len) + throw new DataError('Wrapping key size must be ' + len + ' bytes'); + // 2) Decompose the wrapped content-encryption key into UKM, CEK_ENC, + // and CEK_MAC. UKM is the most significant (first) 8 octets. + // CEK_ENC is next 32 octets, and CEK_MAC is the least significant + // (last) 4 octets. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm), + enc = new Uint8Array(d, 0, k), + mac = new Uint8Array(d, k, n >> 1); + // 3) Diversify KEK using the CryptoPro KEK Diversification Algorithm, + // described in section 6.5. Call the result KEK(UKM). + var dek = diversifyKEK.call(this, kek, ukm); + // 4) Decrypt CEK_ENC in ECB mode using KEK(UKM). Call the output CEK. + var cek = decryptECB.call(this, dek, enc); + // 5) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK(UKM), + // CEK), compare the result with CEK_MAC. If they are not equal, + // then it is an error. + var check = verifyMAC.call(this, dek, mac, cek, ukm); + if (!check) + throw new DataError('Error verify MAC of wrapping key'); + return cek; + } // + + /** + * SignalCom master key packing algorithm + * + * kek stored in 3 files - kek.opq, mk.db3, masks.db3 + * kek.opq - always 36 bytes length = 32 bytes encrypted kek + 4 bytes mac of decrypted kek + * mk.db3 - 6 bytes header (1 byte magic code 0x22 + 1 byte count of masks + 4 bytes mac of + * xor summarizing masks value) + attached masks + * masks.db3 - detached masks. + * Total length of attached + detached masks = 32 bits * count of masks + * Default value of count 8 = (7 attached + 1 detached). But really no reason for such + * separation - all masks xor summarizing - order is not matter. + * Content of file rand.opq can used as ukm. Don't forget change file content after using. + * + * For usb-token files has names: + * a001 - mk.db3, b001 - masks.db3, c001 - kek.opq, d001 - rand.opq + * For windows registry + * 00000001 - mk.db3, 00000002 - masks.db3, 00000003 - key.opq, 00000004 - rand.opq, + * 00000006 - keys\00000001.key, 0000000A - certificate + * + * @memberOf GostCipher + * @method packKey + * @instance + * @private + * @param {CryptoOperationData} unpacked - clear main key 32 bytes + * @param {CryptoOperationData} ukm - random vector for packing - 32 bytes * (count of masks - 1) + * @returns {CryptoOperationData} packed master key - concatination of mk.db3 + masks.db3 + */ + function packKeySC(unpacked, ukm) // + { + var m = this.blockSize >> 1, k = this.keySize; + var mcount = 8; + var key = new Uint8Array(buffer(unpacked)); + if (key.byteLength !== k) + throw new DataError('Wrong cleartext size ' + key.byteLength + ' bytes'); + // Check or generate UKM + ukm = ukm || this.ukm; + if (ukm) { + ukm = new Uint8Array(buffer(ukm)); + if (ukm.byteLength > 0 && ukm.byteLength % k === 0) + mcount = ukm.byteLength / k + 1; + else + throw new DataError('Wrong rand size ' + ukm.byteLength + ' bytes'); + } else + randomSeed(ukm = new Uint8Array((mcount - 1) * k)); + // Output array + var d = new Uint8Array(mcount * k + m + 2), b = d.buffer; + // Calculate MAC + var zero32 = new Uint8Array(k); + var mac = signMAC.call(this, key, zero32); + d[0] = 0x22; // Magic code + d[1] = mcount; // Count of masks + d.set(new Uint8Array(mac), 2); + d.set(ukm, k + m + 2); + for (var i = 1; i < mcount; i++) { + var mask = new Uint8Array(b, 2 + m + k * i); + for (var j = 0; j < k; j++) + key[j] ^= mask[j]; + } + d.set(key, m + 2); + return d.buffer; + } // + + /** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom master key unpacking algorithm + * + * @memberOf GostCipher + * @method unpackKey + * @instance + * @private + * @param {CryptoOperationData} packed - concatination of mk.db3 + masks.db3 + * @returns {CryptoOperationData} unpacked master key + */ + function unpackKeySC(packed) // + { + var m = this.blockSize >> 1, k = this.keySize; + var b = buffer(packed); + // Unpack master key + var magic = new Uint8Array(b, 0, 1)[0]; + if (magic !== 0x22) + throw new DataError('Invalid magic number'); + var mcount = new Uint8Array(b, 1, 1)[0]; + var mac = new Uint8Array(b, 2, m); // MAC for summarized mask + // Compute packKey xor summing for all masks + var key = new Uint8Array(k); + for (var i = 0; i < mcount; i++) { + var mask = new Uint8Array(b, 2 + m + k * i, k); + for (var j = 0; j < k; j++) + key[j] ^= mask[j]; + } + // Test MAC for packKey with default sBox on zero 32 bytes array + var zero32 = new Uint8Array(k); + var test = verifyMAC.call(this, key, mac, zero32); + if (!test) { + // Try to use different sBoxes + var names = ['E-A', 'E-B', 'E-C', 'E-D', 'E-SC']; + for (var i = 0, n = names.length; i < n; i++) { + this.sBox = sBoxes[names[i]]; + test = verifyMAC.call(this, key, mac, zero32); + if (test) + break; + } + } + if (!test) + throw new DataError('Invalid main key MAC'); + return key.buffer; + } // + + /** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom Key Wrapping algorithm + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} kek - clear kek or concatination of mk.db3 + masks.db3 + * @param {CryptoOperationData} cek - key for wrapping + * @returns {CryptoOperationData} wrapped key - file kek.opq + */ + function wrapKeySC(kek, cek) // + { + var m = this.blockSize >> 1, n = this.keySize; + var k = buffer(kek); + var c = buffer(cek); + if (k.byteLength !== n) + k = unpackKeySC.call(this, k); + var enc = encryptECB.call(this, k, c); + var mac = signMAC.call(this, k, c); + var d = new Uint8Array(m + n); + d.set(new Uint8Array(enc), 0); + d.set(new Uint8Array(mac), n); + return d.buffer; + } // + + /** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom Key UnWrapping algorithm + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {CryptoOperationData} kek - concatination of files mk.db3 + masks.db3 or clear kek + * @param {CryptoOperationData} cek - wrapping key - file kek.opq + * @return {CryptoOperationData} result + */ + function unwrapKeySC(kek, cek) // + { + var m = this.blockSize >> 1, n = this.keySize; + var k = buffer(kek); + var c = buffer(cek); + if (k.byteLength !== n) + k = unpackKeySC.call(this, k); + var enc = new Uint8Array(c, 0, n); // Encrypted kek + var mac = new Uint8Array(c, n, m); // MAC for clear kek + var d = decryptECB.call(this, k, enc); + if (!verifyMAC.call(this, k, mac, d)) + throw new DataError('Invalid key MAC'); + return d; + } // + + /** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom master key generation for wrapping + * + * @memberOf GostCipher + * @method generateKey + * @instance + * @return {CryptoOperationData} result + */ + function generateWrappingKeySC() // + { + return packKeySC.call(this, generateKey.call(this)); + } // + + function maskKey(mask, key, inverse, keySize) // + { + var k = keySize / 4, + m32 = new Int32Array(buffer(mask)), + k32 = new Int32Array(buffer(key)), + r32 = new Int32Array(k); + if (inverse) + for (var i = 0; i < k; i++) + r32[i] = (k32[i] + m32[i]) & 0xffffffff; + else + for (var i = 0; i < k; i++) + r32[i] = (k32[i] - m32[i]) & 0xffffffff; + return r32.buffer; + } // + + /** + * Algorithm name GOST 28147-MASK

+ * + * This algorithm wrap key mask + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} mask The mask + * @param {CryptoOperationData} key The key + * @returns {CryptoOperationData} The masked key + */ + function wrapKeyMask(mask, key) // + { + return maskKey(mask, key, this.procreator === 'VN', this.keySize); + } // + + /** + * Algorithm name GOST 28147-CPKW

+ * + * This algorithm unwrap key mask + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {CryptoOperationData} mask The mask + * @param {CryptoOperationData} key The masked key + * @return {CryptoOperationData} result The key + */ + function unwrapKeyMask(mask, key) // + { + return maskKey(mask, key, this.procreator !== 'VN', this.keySize); + } // + + /** + * Algorithm name GOST 28147-CPKM

+ * + * Key meshing in according to rfc4357 2.3.2. CryptoPro Key Meshing + * + * @memberOf GostCipher + * @method keyMeshing + * @instance + * @private + * @param {(Uint8Array|CryptoOperationData)} k 8x8 bit key + * @param {Uint8Array} s 8x8 bit sync (iv) + * @param {Integer} i block index + * @param {Int32Array} key 8x32 bit key schedule + * @param {boolean} e true - decrypt + * @returns CryptoOperationData next 8x8 bit key + */ + function keyMeshingCP(k, s, i, key, e) // + { + if ((i + 1) * this.blockSize % 1024 === 0) { // every 1024 octets + // K[i+1] = decryptECB (K[i], C); + k = decryptECB.call(this, k, C); + // IV0[i+1] = encryptECB (K[i+1],IVn[i]) + s.set(new Uint8Array(encryptECB.call(this, k, s))); + // restore key schedule + key.set(this.keySchedule(k, e)); + } + return k; + } // + + /** + * Null Key Meshing in according to rfc4357 2.3.1 + * + * @memberOf GostCipher + * @method keyMeshing + * @instance + * @private + * @param {(Uint8Array|CryptoOperationData)} k 8x8 bit key + */ + function noKeyMeshing(k) // + { + return k; + } // + + /** + * Algorithm name GOST 28147-NoPadding

+ * + * No padding. + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ + function noPad(d) // + { + return new Uint8Array(d); + } // + + /** + * Algorithm name GOST 28147-PKCS5Padding

+ * + * PKCS#5 padding: 8-x remaining bytes are filled with the value of + * 8-x. If there’s no incomplete block, one extra block filled with + * value 8 is added + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ + function pkcs5Pad(d) // + { + var n = d.byteLength, + nb = this.blockSize, + q = nb - n % nb, + m = Math.ceil((n + 1) / nb) * nb, + r = new Uint8Array(m); + r.set(d); + for (var i = n; i < m; i++) + r[i] = q; + return r; + } // + + function pkcs5Unpad(d) // + { + var m = d.byteLength, + nb = this.blockSize, + q = d[m - 1], + n = m - q; + if (q > nb) + throw DataError('Invalid padding'); + var r = new Uint8Array(n); + if (n > 0) + r.set(new Uint8Array(d.buffer, 0, n)); + return r; + } // + + + /** + * Algorithm name GOST 28147-ZeroPadding

+ * + * Zero padding: 8-x remaining bytes are filled with zero + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ + function zeroPad(d) // + { + var n = d.byteLength, + nb = this.blockSize, + m = Math.ceil(n / nb) * nb, + r = new Uint8Array(m); + r.set(d); + for (var i = n; i < m; i++) + r[i] = 0; + return r; + } // + + + /** + * Algorithm name GOST 28147-BitPadding

+ * + * Bit padding: P* = P || 1 || 000...0 If there’s no incomplete block, + * one extra block filled with 1 || 000...0 + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ + function bitPad(d) // + { + var n = d.byteLength, + nb = this.blockSize, + m = Math.ceil((n + 1) / nb) * nb, + r = new Uint8Array(m); + r.set(d); + r[n] = 1; + for (var i = n + 1; i < m; i++) + r[i] = 0; + return r; + } // + + function bitUnpad(d) // + { + var m = d.byteLength, + n = m; + while (n > 1 && d[n - 1] === 0) + n--; + if (d[n - 1] !== 1) + throw DataError('Invalid padding'); + n--; + var r = new Uint8Array(n); + if (n > 0) + r.set(new Uint8Array(d.buffer, 0, n)); + return r; + } // + + /** + * Algorithm name GOST 28147-RandomPadding

+ * + * Random padding: 8-x remaining bytes of the last block are set to + * random. + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ + function randomPad(d) // + { + var n = d.byteLength, + nb = this.blockSize, + q = nb - n % nb, + m = Math.ceil(n / nb) * nb, + r = new Uint8Array(m), e = new Uint8Array(r.buffer, n, q); + r.set(d); + randomSeed(e); + return r; + } // + + /** + * GOST 28147-89 Encryption Algorithm

+ * + * References {@link http://tools.ietf.org/html/rfc5830}

+ * + * When keys and initialization vectors are converted to/from byte arrays, + * little-endian byte order is assumed.

+ * + * Normalized algorithm identifier common parameters: + * + * + * + * Supported algorithms, modes and parameters: + * + * + * + * Supported paramters values: + * + * + * + * @class GostCipher + * @param {AlgorithmIndentifier} algorithm WebCryptoAPI algorithm identifier + */ + function GostCipher(algorithm) // + { + // Check little endian support + if (!littleEndian) + throw new NotSupportedError('Big endian platform not supported'); + algorithm = algorithm || {}; + this.keySize = 32; + this.blockLength = algorithm.length || 64; + this.blockSize = this.blockLength >> 3; + + this.name = (algorithm.name || (algorithm.version === 1 ? 'RC2' : + algorithm.version === 1989 ? 'GOST 28147' : 'GOST R 34.12')) + + (algorithm.version > 4 ? '-' + ((algorithm.version || 1989) % 100) : '') + '-' + + (this.blockLength === 64 ? '' : this.blockLength + '-') + + ((algorithm.mode === 'MAC') ? 'MAC-' + (algorithm.macLength || this.blockLength >> 1) : + (algorithm.mode === 'KW' || algorithm.keyWrapping) ? + ((algorithm.keyWrapping || 'NO') !== 'NO' ? algorithm.keyWrapping : '') + 'KW' : + (algorithm.block || 'ECB') + ((algorithm.block === 'CFB' || algorithm.block === 'OFB' || + (algorithm.block === 'CTR' && algorithm.version === 2015)) && + algorithm.shiftBits && algorithm.shiftBits !== this.blockLength ? '-' + algorithm.shiftBits : '') + + (algorithm.padding ? '-' + (algorithm.padding || (algorithm.block === 'CTR' || + algorithm.block === 'CFB' || algorithm.block === 'OFB' ? 'NO' : 'ZERO')) + 'PADDING' : '') + + ((algorithm.keyMeshing || 'NO') !== 'NO' ? '-CPKEYMESHING' : '')) + + (algorithm.procreator ? '/' + algorithm.procreator : '') + + (typeof algorithm.sBox === 'string' ? '/' + algorithm.sBox : ''); + + // Algorithm procreator + this.procreator = algorithm.procreator; + + switch (algorithm.version || 1989) { + case 1: + this.process = processRC2; + this.keySchedule = keyScheduleRC2; + this.blockLength = 64; + this.effectiveLength = algorithm.length || 32; + this.keySize = 8 * Math.ceil(this.effectiveLength / 8); // Max 128 + this.blockSize = this.blockLength >> 3; + break; + case 2015: + this.version = 2015; + if (this.blockLength === 64) { + this.process = process15; + this.keySchedule = keySchedule15; + } else if (this.blockLength === 128) { + this.process = process128; + this.keySchedule = keySchedule128; + } else + throw new DataError('Invalid block length'); + this.processMAC = processMAC15; + break; + case 1989: + this.version = 1989; + this.process = process89; + this.processMAC = processMAC89; + this.keySchedule = keySchedule89; + if (this.blockLength !== 64) + throw new DataError('Invalid block length'); + break; + default: + throw new NotSupportedError('Algorithm version ' + algorithm.version + ' not supported'); + } + + switch (algorithm.mode || (algorithm.keyWrapping && 'KW') || 'ES') { + + case 'ES': + switch (algorithm.block || 'ECB') { + case 'ECB': + this.encrypt = encryptECB; + this.decrypt = decryptECB; + break; + case 'CTR': + if (this.version === 1989) { + this.encrypt = processCTR89; + this.decrypt = processCTR89; + } else { + this.encrypt = processCTR15; + this.decrypt = processCTR15; + this.shiftBits = algorithm.shiftBits || this.blockLength; + } + break + case 'CBC': + this.encrypt = encryptCBC; + this.decrypt = decryptCBC; + break + case 'CFB': + this.encrypt = encryptCFB; + this.decrypt = decryptCFB; + this.shiftBits = algorithm.shiftBits || this.blockLength; + break; + case 'OFB': + this.encrypt = processOFB; + this.decrypt = processOFB; + this.shiftBits = algorithm.shiftBits || this.blockLength; + break; + default: + throw new NotSupportedError('Block mode ' + algorithm.block + ' not supported'); + } + switch (algorithm.keyMeshing) { + case 'CP': + this.keyMeshing = keyMeshingCP; + break; + default: + this.keyMeshing = noKeyMeshing; + } + if (this.encrypt === encryptECB || this.encrypt === encryptCBC) { + switch (algorithm.padding) { + case 'PKCS5P': + this.pad = pkcs5Pad; + this.unpad = pkcs5Unpad; + break; + case 'RANDOM': + this.pad = randomPad; + this.unpad = noPad; + break; + case 'BIT': + this.pad = bitPad; + this.unpad = bitUnpad; + break; + default: + this.pad = zeroPad; + this.unpad = noPad; + } + } else { + this.pad = noPad; + this.unpad = noPad; + } + this.generateKey = generateKey; + break; + case 'MAC': + this.sign = signMAC; + this.verify = verifyMAC; + this.generateKey = generateKey; + this.macLength = algorithm.macLength || (this.blockLength >> 1); + this.pad = noPad; + this.unpad = noPad; + this.keyMeshing = noKeyMeshing; + break; + case 'KW': + this.pad = noPad; + this.unpad = noPad; + this.keyMeshing = noKeyMeshing; + switch (algorithm.keyWrapping) { + case 'CP': + this.wrapKey = wrapKeyCP; + this.unwrapKey = unwrapKeyCP; + this.generateKey = generateKey; + this.shiftBits = algorithm.shiftBits || this.blockLength; + break; + case 'SC': + this.wrapKey = wrapKeySC; + this.unwrapKey = unwrapKeySC; + this.generateKey = generateWrappingKeySC; + break; + default: + this.wrapKey = wrapKeyGOST; + this.unwrapKey = unwrapKeyGOST; + this.generateKey = generateKey; + } + break; + case 'MASK': + this.wrapKey = wrapKeyMask; + this.unwrapKey = unwrapKeyMask; + this.generateKey = generateKey; + break; + default: + throw new NotSupportedError('Mode ' + algorithm.mode + ' not supported'); + } + + // Define sBox parameter + var sBox = algorithm.sBox, sBoxName; + if (!sBox) + sBox = this.version === 2015 ? sBoxes['E-Z'] : this.procreator === 'SC' ? sBoxes['E-SC'] : sBoxes['E-A']; + else if (typeof sBox === 'string') { + sBoxName = sBox.toUpperCase(); + sBox = sBoxes[sBoxName]; + if (!sBox) + throw new SyntaxError('Unknown sBox name: ' + algorithm.sBox); + } else if (!sBox.length || sBox.length !== sBoxes['E-Z'].length) + throw new SyntaxError('Length of sBox must be ' + sBoxes['E-Z'].length); + this.sBox = sBox; + // Initial vector + if (algorithm.iv) { + this.iv = new Uint8Array(algorithm.iv); + if (this.iv.byteLength !== this.blockSize && this.version === 1989) + throw new SyntaxError('Length of iv must be ' + this.blockLength + ' bits'); + else if (this.iv.byteLength !== this.blockSize >> 1 && this.encrypt === processCTR15) + throw new SyntaxError('Length of iv must be ' + this.blockLength >> 1 + ' bits'); + else if (this.iv.byteLength % this.blockSize !== 0 && this.encrypt !== processCTR15) + throw new SyntaxError('Length of iv must be a multiple of ' + this.blockLength + ' bits'); + } else + this.iv = this.blockLength === 128 ? defaultIV128 : defaultIV; + // User key material + if (algorithm.ukm) { + this.ukm = new Uint8Array(algorithm.ukm); + if (this.ukm.byteLength * 8 !== this.blockLength) + throw new SyntaxError('Length of ukm must be ' + this.blockLength + ' bits'); + } + } // + + return GostCipher; +})); + diff --git a/src/core/vendor/streebog/gostCoding.js b/src/core/vendor/streebog/gostCoding.js new file mode 100644 index 00000000..cc909c12 --- /dev/null +++ b/src/core/vendor/streebog/gostCoding.js @@ -0,0 +1,1171 @@ +/** + * @file Coding algorithms: Base64, Hex, Int16, Chars, BER and PEM + * @version 1.76 + * @copyright 2014-2016, Rudolf Nickolaev. All rights reserved. + */ + +/* + * 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. + * + * 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. + * + */ + +(function (root, factory) { + + /* + * Module imports and exports + * + */ // + if (typeof exports === 'object') { + module.exports = factory(require('./gostCrypto')); + } + // + +}(this, function (gostCrypto) { + + /** + * The Coding interface provides string converting methods: Base64, Hex, + * Int16, Chars, BER and PEM + * @class GostCoding + * + */ // + var root = this; + var DataError = root.DataError || root.Error; + var CryptoOperationData = root.ArrayBuffer; + var Date = root.Date; + + function buffer(d) { + if (d instanceof CryptoOperationData) + return d; + else if (d && d.buffer && d.buffer instanceof CryptoOperationData) + 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('CryptoOperationData required'); + } // + + function GostCoding() { + } + + /** + * BASE64 conversion + * + * @class GostCoding.Base64 + */ + var Base64 = {// + /** + * Base64.decode convert BASE64 string s to CryptoOperationData + * + * @memberOf GostCoding.Base64 + * @param {String} s BASE64 encoded string value + * @returns {CryptoOperationData} Binary decoded data + */ + decode: function (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; + }, + /** + * Base64.encode(data) convert CryptoOperationData data to BASE64 string + * + * @memberOf GostCoding.Base64 + * @param {CryptoOperationData} data Bynary data for encoding + * @returns {String} BASE64 encoded data + */ + encode: function (data) { + var slen = 8, d = new Uint8Array(buffer(data)); + var m3 = 2, s = ''; + for (var n = d.length, u24 = 0, i = 0; i < n; i++) { + m3 = i % 3; + if (i > 0 && (i * 4 / 3) % (12 * slen) === 0) + s += '\r\n'; + u24 |= d[i] << (16 >>> m3 & 24); + if (m3 === 2 || n - i === 1) { + for (var j = 18; j >= 0; j -= 6) { + var c = u24 >>> j & 63; + c = c < 26 ? c + 65 : c < 52 ? c + 71 : c < 62 ? c - 4 : + c === 62 ? 43 : c === 63 ? 47 : 65; + s += String.fromCharCode(c); + } + u24 = 0; + } + } + return s.substr(0, s.length - 2 + m3) + (m3 === 2 ? '' : m3 === 1 ? '=' : '=='); + } // + }; + + /** + * BASE64 conversion + * + * @memberOf GostCoding + * @insnance + * @type GostCoding.Base64 + */ + GostCoding.prototype.Base64 = Base64; + + /** + * Text string conversion
+ * Methods support charsets: ascii, win1251, utf8, utf16 (ucs2, unicode), utf32 (ucs4) + * + * @class GostCoding.Chars + */ + var Chars = (function () { // + + var _win1251_ = { + 0x402: 0x80, 0x403: 0x81, 0x201A: 0x82, 0x453: 0x83, 0x201E: 0x84, 0x2026: 0x85, 0x2020: 0x86, 0x2021: 0x87, + 0x20AC: 0x88, 0x2030: 0x89, 0x409: 0x8A, 0x2039: 0x8B, 0x40A: 0x8C, 0x40C: 0x8D, 0x40B: 0x8E, 0x40f: 0x8f, + 0x452: 0x90, 0x2018: 0x91, 0x2019: 0x92, 0x201C: 0x93, 0x201D: 0x94, 0x2022: 0x95, 0x2013: 0x96, 0x2014: 0x97, + 0x2122: 0x99, 0x459: 0x9A, 0x203A: 0x9B, 0x45A: 0x9C, 0x45C: 0x9D, 0x45B: 0x9E, 0x45f: 0x9f, + 0xA0: 0xA0, 0x40E: 0xA1, 0x45E: 0xA2, 0x408: 0xA3, 0xA4: 0xA4, 0x490: 0xA5, 0xA6: 0xA6, 0xA7: 0xA7, + 0x401: 0xA8, 0xA9: 0xA9, 0x404: 0xAA, 0xAB: 0xAB, 0xAC: 0xAC, 0xAD: 0xAD, 0xAE: 0xAE, 0x407: 0xAf, + 0xB0: 0xB0, 0xB1: 0xB1, 0x406: 0xB2, 0x456: 0xB3, 0x491: 0xB4, 0xB5: 0xB5, 0xB6: 0xB6, 0xB7: 0xB7, + 0x451: 0xB8, 0x2116: 0xB9, 0x454: 0xBA, 0xBB: 0xBB, 0x458: 0xBC, 0x405: 0xBD, 0x455: 0xBE, 0x457: 0xBf + }; + var _win1251back_ = {}; + for (var from in _win1251_) { + var to = _win1251_[from]; + _win1251back_[to] = from; + } + + return { + /** + * Chars.decode(s, charset) convert string s with defined charset to CryptoOperationData + * + * @memberOf GostCoding.Chars + * @param {string} s Javascript string + * @param {string} charset Charset, default 'win1251' + * @returns {CryptoOperationData} Decoded binary data + */ + decode: function (s, charset) { + charset = (charset || 'win1251').toLowerCase().replace('-', ''); + var r = []; + for (var i = 0, j = s.length; i < j; i++) { + var c = s.charCodeAt(i); + if (charset === 'utf8') { + if (c < 0x80) { + r.push(c); + } else if (c < 0x800) { + r.push(0xc0 + (c >>> 6)); + r.push(0x80 + (c & 63)); + } else if (c < 0x10000) { + r.push(0xe0 + (c >>> 12)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } else if (c < 0x200000) { + r.push(0xf0 + (c >>> 18)); + r.push(0x80 + (c >>> 12 & 63)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } else if (c < 0x4000000) { + r.push(0xf8 + (c >>> 24)); + r.push(0x80 + (c >>> 18 & 63)); + r.push(0x80 + (c >>> 12 & 63)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } else { + r.push(0xfc + (c >>> 30)); + r.push(0x80 + (c >>> 24 & 63)); + r.push(0x80 + (c >>> 18 & 63)); + r.push(0x80 + (c >>> 12 & 63)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } + } else if (charset === 'unicode' || charset === 'ucs2' || charset === 'utf16') { + if (c < 0xD800 || (c >= 0xE000 && c <= 0x10000)) { + r.push(c >>> 8); + r.push(c & 0xff); + } else if (c >= 0x10000 && c < 0x110000) { + c -= 0x10000; + var first = ((0xffc00 & c) >> 10) + 0xD800; + var second = (0x3ff & c) + 0xDC00; + r.push(first >>> 8); + r.push(first & 0xff); + r.push(second >>> 8); + r.push(second & 0xff); + } + } else if (charset === 'utf32' || charset === 'ucs4') { + r.push(c >>> 24 & 0xff); + r.push(c >>> 16 & 0xff); + r.push(c >>> 8 & 0xff); + r.push(c & 0xff); + } else if (charset === 'win1251') { + if (c >= 0x80) { + if (c >= 0x410 && c < 0x450) // А..Яа..я + c -= 0x350; + else + c = _win1251_[c] || 0; + } + r.push(c); + } else + r.push(c & 0xff); + } + return new Uint8Array(r).buffer; + }, + /** + * Chars.encode(data, charset) convert CryptoOperationData data to string with defined charset + * + * @memberOf GostCoding.Chars + * @param {CryptoOperationData} data Binary data + * @param {string} charset Charset, default win1251 + * @returns {string} Encoded javascript string + */ + encode: function (data, charset) { + charset = (charset || 'win1251').toLowerCase().replace('-', ''); + var r = [], d = new Uint8Array(buffer(data)); + for (var i = 0, n = d.length; i < n; i++) { + var c = d[i]; + if (charset === 'utf8') { + c = c >= 0xfc && c < 0xfe && i + 5 < n ? // six bytes + (c - 0xfc) * 1073741824 + (d[++i] - 0x80 << 24) + (d[++i] - 0x80 << 18) + (d[++i] - 0x80 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >> 0xf8 && c < 0xfc && i + 4 < n ? // five bytes + (c - 0xf8 << 24) + (d[++i] - 0x80 << 18) + (d[++i] - 0x80 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >> 0xf0 && c < 0xf8 && i + 3 < n ? // four bytes + (c - 0xf0 << 18) + (d[++i] - 0x80 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >= 0xe0 && c < 0xf0 && i + 2 < n ? // three bytes + (c - 0xe0 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >= 0xc0 && c < 0xe0 && i + 1 < n ? // two bytes + (c - 0xc0 << 6) + d[++i] - 0x80 + : c; // one byte + } else if (charset === 'unicode' || charset === 'ucs2' || charset === 'utf16') { + c = (c << 8) + d[++i]; + if (c >= 0xD800 && c < 0xE000) { + var first = (c - 0xD800) << 10; + c = d[++i]; + c = (c << 8) + d[++i]; + var second = c - 0xDC00; + c = first + second + 0x10000; + } + } else if (charset === 'utf32' || charset === 'ucs4') { + c = (c << 8) + d[++i]; + c = (c << 8) + d[++i]; + c = (c << 8) + d[++i]; + } else if (charset === 'win1251') { + if (c >= 0x80) { + if (c >= 0xC0 && c < 0x100) + c += 0x350; // А..Яа..я + else + c = _win1251back_[c] || 0; + } + } + r.push(String.fromCharCode(c)); + } + return r.join(''); + } + }; // + })(); + + /** + * Text string conversion + * + * @memberOf GostCoding + * @insnance + * @type GostCoding.Chars + */ + GostCoding.prototype.Chars = Chars; + + /** + * HEX conversion + * + * @class GostCoding.Hex + */ + var Hex = {// + /** + * Hex.decode(s, endean) convert HEX string s to CryptoOperationData in endean mode + * + * @memberOf GostCoding.Hex + * @param {string} s Hex encoded string + * @param {boolean} endean Little or Big Endean, default Little + * @returns {CryptoOperationData} Decoded binary data + */ + decode: function (s, endean) { + s = s.replace(/[^A-fa-f0-9]/g, ''); + var n = Math.ceil(s.length / 2), r = new Uint8Array(n); + s = (s.length % 2 > 0 ? '0' : '') + s; + if (endean && ((typeof endean !== 'string') || + (endean.toLowerCase().indexOf('little') < 0))) + for (var i = 0; i < n; i++) + r[i] = parseInt(s.substr((n - i - 1) * 2, 2), 16); + else + for (var i = 0; i < n; i++) + r[i] = parseInt(s.substr(i * 2, 2), 16); + return r.buffer; + }, + /** + * Hex.encode(data, endean) convert CryptoOperationData data to HEX string in endean mode + * + * @memberOf GostCoding.Hex + * @param {CryptoOperationData} data Binary data + * @param {boolean} endean Little/Big Endean, default Little + * @returns {string} Hex decoded string + */ + encode: function (data, endean) { + var s = [], d = new Uint8Array(buffer(data)), n = d.length; + if (endean && ((typeof endean !== 'string') || + (endean.toLowerCase().indexOf('little') < 0))) + for (var i = 0; i < n; i++) { + var j = n - i - 1; + s[j] = (j > 0 && j % 32 === 0 ? '\r\n' : '') + + ('00' + d[i].toString(16)).slice(-2); + } + else + for (var i = 0; i < n; i++) + s[i] = (i > 0 && i % 32 === 0 ? '\r\n' : '') + + ('00' + d[i].toString(16)).slice(-2); + return s.join(''); + } // + }; + + /** + * HEX conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.Hex + */ + GostCoding.prototype.Hex = Hex; + + /** + * String hex-encoded integer conversion + * + * @class GostCoding.Int16 + */ + var Int16 = {// + /** + * Int16.decode(s) convert hex big insteger s to CryptoOperationData + * + * @memberOf GostCoding.Int16 + * @param {string} s Int16 string + * @returns {CryptoOperationData} Decoded binary data + */ + decode: function (s) { + s = (s || '').replace(/[^\-A-fa-f0-9]/g, ''); + if (s.length === 0) + s = '0'; + // Signature + var neg = false; + if (s.charAt(0) === '-') { + neg = true; + s = s.substring(1); + } + // Align 2 chars + while (s.charAt(0) === '0' && s.length > 1) + s = s.substring(1); + s = (s.length % 2 > 0 ? '0' : '') + s; + // Padding for singanuture + // '800000' - 'ffffff' - for positive + // '800001' - 'ffffff' - for negative + if ((!neg && !/^[0-7]/.test(s)) || + (neg && !/^[0-7]|8[0]+$/.test(s))) + s = '00' + s; + // Convert hex + var n = s.length / 2, r = new Uint8Array(n), t = 0; + for (var i = n - 1; i >= 0; --i) { + var c = parseInt(s.substr(i * 2, 2), 16); + if (neg && (c + t > 0)) { + c = 256 - c - t; + t = 1; + } + r[i] = c; + } + return r.buffer; + }, + /** + * Int16.encode(data) convert CryptoOperationData data to big integer hex string + * + * @memberOf GostCoding.Int16 + * @param {CryptoOperationData} data Binary data + * @returns {string} Int16 encoded string + */ + encode: function (data) { + var d = new Uint8Array(buffer(data)), n = d.length; + if (d.length === 0) + return '0x00'; + var s = [], neg = d[0] > 0x7f, t = 0; + for (var i = n - 1; i >= 0; --i) { + var v = d[i]; + if (neg && (v + t > 0)) { + v = 256 - v - t; + t = 1; + } + s[i] = ('00' + v.toString(16)).slice(-2); + } + s = s.join(''); + while (s.charAt(0) === '0') + s = s.substring(1); + return (neg ? '-' : '') + '0x' + s; + } // + }; + + /** + * String hex-encoded integer conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.Int16 + */ + GostCoding.prototype.Int16 = Int16; + + /** + * BER, DER, CER conversion + * + * @class GostCoding.BER + */ + var BER = (function () { // + + // Predefenition block + function encodeBER(source, format, onlyContent) { + // Correct primitive type + var object = source.object; + if (object === undefined) + object = source; + + // Determinate tagClass + var tagClass = source.tagClass = source.tagClass || 0; // Universial default + + // Determinate tagNumber. Use only for Universal class + if (tagClass === 0) { + var tagNumber = source.tagNumber; + if (typeof tagNumber === 'undefined') { + if (typeof object === 'string') { + if (object === '') // NULL + tagNumber = 0x05; + else if (/^\-?0x[0-9a-fA-F]+$/.test(object)) // INTEGER + tagNumber = 0x02; + else if (/^(\d+\.)+\d+$/.test(object)) // OID + tagNumber = 0x06; + else if (/^[01]+$/.test(object)) // BIT STRING + tagNumber = 0x03; + else if (/^(true|false)$/.test(object)) // BOOLEAN + tagNumber = 0x01; + else if (/^[0-9a-fA-F]+$/.test(object)) // OCTET STRING + tagNumber = 0x04; + else + tagNumber = 0x13; // Printable string (later can be changed to UTF8String) + } else if (typeof object === 'number') { // INTEGER + tagNumber = 0x02; + } else if (typeof object === 'boolean') { // BOOLEAN + tagNumber = 0x01; + } else if (object instanceof Array) { // SEQUENCE + tagNumber = 0x10; + } else if (object instanceof Date) { // GeneralizedTime + tagNumber = 0x18; + } else if (object instanceof CryptoOperationData || (object && object.buffer instanceof CryptoOperationData)) { + tagNumber = 0x04; + } else + throw new DataError('Unrecognized type for ' + object); + } + } + + // Determinate constructed + var tagConstructed = source.tagConstructed; + if (typeof tagConstructed === 'undefined') + tagConstructed = source.tagConstructed = object instanceof Array; + + // Create content + var content; + if (object instanceof CryptoOperationData || (object && object.buffer instanceof CryptoOperationData)) { // Direct + content = new Uint8Array(buffer(object)); + if (tagNumber === 0x03) { // BITSTRING + // Set unused bits + var a = new Uint8Array(buffer(content)); + content = new Uint8Array(a.length + 1); + content[0] = 0; // No unused bits + content.set(a, 1); + } + } else if (tagConstructed) { // Sub items coding + if (object instanceof Array) { + var bytelen = 0, ba = [], offset = 0; + for (var i = 0, n = object.length; i < n; i++) { + ba[i] = encodeBER(object[i], format); + bytelen += ba[i].length; + } + if (tagNumber === 0x11) + ba.sort(function (a, b) { // Sort order for SET components + for (var i = 0, n = Math.min(a.length, b.length); i < n; i++) { + var r = a[i] - b[i]; + if (r !== 0) + return r; + } + return a.length - b.length; + }); + if (format === 'CER') { // final for CER 00 00 + ba[n] = new Uint8Array(2); + bytelen += 2; + } + content = new Uint8Array(bytelen); + for (var i = 0, n = ba.length; i < n; i++) { + content.set(ba[i], offset); + offset = offset + ba[i].length; + } + } else + throw new DataError('Constracted block can\'t be primitive'); + } else { + switch (tagNumber) { + // 0x00: // EOC + case 0x01: // BOOLEAN + content = new Uint8Array(1); + content[0] = object ? 0xff : 0; + break; + case 0x02: // INTEGER + case 0x0a: // ENUMIRATED + content = Int16.decode( + typeof object === 'number' ? object.toString(16) : object); + break; + case 0x03: // BIT STRING + if (typeof object === 'string') { + var unusedBits = 7 - (object.length + 7) % 8; + var n = Math.ceil(object.length / 8); + content = new Uint8Array(n + 1); + content[0] = unusedBits; + for (var i = 0; i < n; i++) { + var c = 0; + for (var j = 0; j < 8; j++) { + var k = i * 8 + j; + c = (c << 1) + (k < object.length ? (object.charAt(k) === '1' ? 1 : 0) : 0); + } + content[i + 1] = c; + } + } + break; + case 0x04: + content = Hex.decode( + typeof object === 'number' ? object.toString(16) : object); + break; + // case 0x05: // NULL + case 0x06: // OBJECT IDENTIFIER + var a = object.match(/\d+/g), r = []; + for (var i = 1; i < a.length; i++) { + var n = +a[i], r1 = []; + if (i === 1) + n = n + a[0] * 40; + do { + r1.push(n & 0x7F); + n = n >>> 7; + } while (n); + // reverse order + for (j = r1.length - 1; j >= 0; --j) + r.push(r1[j] + (j === 0 ? 0x00 : 0x80)); + } + content = new Uint8Array(r); + break; + // case 0x07: // ObjectDescriptor + // case 0x08: // EXTERNAL + // case 0x09: // REAL + // case 0x0A: // ENUMERATED + // case 0x0B: // EMBEDDED PDV + case 0x0C: // UTF8String + content = Chars.decode(object, 'utf8'); + break; + // case 0x10: // SEQUENCE + // case 0x11: // SET + case 0x12: // NumericString + case 0x16: // IA5String // ASCII + case 0x13: // PrintableString // ASCII subset + case 0x14: // TeletexString // aka T61String + case 0x15: // VideotexString + case 0x19: // GraphicString + case 0x1A: // VisibleString // ASCII subset + case 0x1B: // GeneralString + // Reflect on character encoding + for (var i = 0, n = object.length; i < n; i++) + if (object.charCodeAt(i) > 255) + tagNumber = 0x0C; + if (tagNumber === 0x0C) + content = Chars.decode(object, 'utf8'); + else + content = Chars.decode(object, 'ascii'); + break; + case 0x17: // UTCTime + case 0x18: // GeneralizedTime + var result = object.original; + if (!result) { + var date = new Date(object); + date.setMinutes(date.getMinutes() + date.getTimezoneOffset()); // to UTC + var ms = tagNumber === 0x18 ? date.getMilliseconds().toString() : ''; // Milliseconds, remove trailing zeros + while (ms.length > 0 && ms.charAt(ms.length - 1) === '0') + ms = ms.substring(0, ms.length - 1); + if (ms.length > 0) + ms = '.' + ms; + result = (tagNumber === 0x17 ? date.getYear().toString().slice(-2) : date.getFullYear().toString()) + + ('00' + (date.getMonth() + 1)).slice(-2) + + ('00' + date.getDate()).slice(-2) + + ('00' + date.getHours()).slice(-2) + + ('00' + date.getMinutes()).slice(-2) + + ('00' + date.getSeconds()).slice(-2) + ms + 'Z'; + } + content = Chars.decode(result, 'ascii'); + break; + case 0x1C: // UniversalString + content = Chars.decode(object, 'utf32'); + break; + case 0x1E: // BMPString + content = Chars.decode(object, 'utf16'); + break; + } + } + + if (!content) + content = new Uint8Array(0); + if (content instanceof CryptoOperationData) + content = new Uint8Array(content); + + if (!tagConstructed && format === 'CER') { + // Encoding CER-form for string types + var k; + switch (tagNumber) { + case 0x03: // BIT_STRING + k = 1; // ingnore unused bit for bit string + case 0x04: // OCTET_STRING + case 0x0C: // UTF8String + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + case 0x19: // GraphicString + case 0x1A: // VisibleString + case 0x1B: // GeneralString + case 0x1C: // UniversalString + case 0x1E: // BMPString + k = k || 0; + // Split content on 1000 octet len parts + var size = 1000; + var bytelen = 0, ba = [], offset = 0; + for (var i = k, n = content.length; i < n; i += size - k) { + ba[i] = encodeBER({ + object: new Unit8Array(content.buffer, i, Math.min(size - k, n - i)), + tagNumber: tagNumber, + tagClass: 0, + tagConstructed: false + }, format); + bytelen += ba[i].length; + } + ba[n] = new Uint8Array(2); // final for CER 00 00 + bytelen += 2; + content = new Uint8Array(bytelen); + for (var i = 0, n = ba.length; i < n; i++) { + content.set(ba[i], offset); + offset = offset + ba[i].length; + } + } + } + + // Restore tagNumber for all classes + if (tagClass === 0) + source.tagNumber = tagNumber; + else + source.tagNumber = tagNumber = source.tagNumber || 0; + source.content = content; + + if (onlyContent) + return content; + + // Create header + // tagNumber + var ha = [], first = tagClass === 3 ? 0xC0 : tagClass === 2 ? 0x80 : + tagClass === 1 ? 0x40 : 0x00; + if (tagConstructed) + first |= 0x20; + if (tagNumber < 0x1F) { + first |= tagNumber & 0x1F; + ha.push(first); + } else { + first |= 0x1F; + ha.push(first); + var n = tagNumber, ha1 = []; + do { + ha1.push(n & 0x7F); + n = n >>> 7; + } while (n) + // reverse order + for (var j = ha1.length - 1; j >= 0; --j) + ha.push(ha1[j] + (j === 0 ? 0x00 : 0x80)); + } + // Length + if (tagConstructed && format === 'CER') { + ha.push(0x80); + } else { + var len = content.length; + if (len > 0x7F) { + var l2 = len, ha2 = []; + do { + ha2.push(l2 & 0xff); + l2 = l2 >>> 8; + } while (l2); + ha.push(ha2.length + 0x80); // reverse order + for (var j = ha2.length - 1; j >= 0; --j) + ha.push(ha2[j]); + } else { + // simple len + ha.push(len); + } + } + var header = source.header = new Uint8Array(ha); + + // Result - complete buffer + var block = new Uint8Array(header.length + content.length); + block.set(header, 0); + block.set(content, header.length); + return block; + } + + function decodeBER(source, offset) { + + // start pos + var pos = offset || 0, start = pos; + var tagNumber, tagClass, tagConstructed, + content, header, buffer, sub, len; + + if (source.object) { + // Ready from source + tagNumber = source.tagNumber; + tagClass = source.tagClass; + tagConstructed = source.tagConstructed; + content = source.content; + header = source.header; + buffer = source.object instanceof CryptoOperationData ? + new Uint8Array(source.object) : null; + sub = source.object instanceof Array ? source.object : null; + len = buffer && buffer.length || null; + } else { + // Decode header + var d = source; + + // Read tag + var buf = d[pos++]; + tagNumber = buf & 0x1f; + tagClass = buf >> 6; + tagConstructed = (buf & 0x20) !== 0; + if (tagNumber === 0x1f) { // long tag + tagNumber = 0; + do { + if (tagNumber > 0x1fffffffffff80) + throw new DataError('Convertor not supported tag number more then (2^53 - 1) at position ' + offset); + buf = d[pos++]; + tagNumber = (tagNumber << 7) + (buf & 0x7f); + } while (buf & 0x80); + } + + // Read len + buf = d[pos++]; + len = buf & 0x7f; + if (len !== buf) { + if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways + throw new DataError('Length over 48 bits not supported at position ' + offset); + if (len === 0) + len = null; // undefined + else { + buf = 0; + for (var i = 0; i < len; ++i) + buf = (buf << 8) + d[pos++]; + len = buf; + } + } + + start = pos; + sub = null; + + if (tagConstructed) { + // must have valid content + sub = []; + if (len !== null) { + // definite length + var end = start + len; + while (pos < end) { + var s = decodeBER(d, pos); + sub.push(s); + pos += s.header.length + s.content.length; + } + if (pos !== end) + throw new DataError('Content size is not correct for container starting at offset ' + start); + } else { + // undefined length + try { + for (; ; ) { + var s = decodeBER(d, pos); + pos += s.header.length + s.content.length; + if (s.tagClass === 0x00 && s.tagNumber === 0x00) + break; + sub.push(s); + } + len = pos - start; + } catch (e) { + throw new DataError('Exception ' + e + ' while decoding undefined length content at offset ' + start); + } + } + } + + // Header and content + header = new Uint8Array(d.buffer, offset, start - offset); + content = new Uint8Array(d.buffer, start, len); + buffer = content; + } + + // Constructed types - check for string concationation + if (sub !== null && tagClass === 0) { + var k; + switch (tagNumber) { + case 0x03: // BIT_STRING + k = 1; // ingnore unused bit for bit string + case 0x04: // OCTET_STRING + case 0x0C: // UTF8String + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + case 0x19: // GraphicString + case 0x1A: // VisibleString + case 0x1B: // GeneralString + case 0x1C: // UniversalString + case 0x1E: // BMPString + k = k || 0; + // Concatination + if (sub.length === 0) + throw new DataError('No constructed encoding content of string type at offset ' + start); + len = k; + for (var i = 0, n = sub.length; i < n; i++) { + var s = sub[i]; + if (s.tagClass !== tagClass || s.tagNumber !== tagNumber || s.tagConstructed) + throw new DataError('Invalid constructed encoding of string type at offset ' + start); + len += s.content.length - k; + } + buffer = new Uint8Array(len); + for (var i = 0, n = sub.length, j = k; i < n; i++) { + var s = sub[i]; + if (k > 0) + buffer.set(s.content.subarray(1), j); + else + buffer.set(s.content, j); + j += s.content.length - k; + } + tagConstructed = false; // follow not required + sub = null; + break; + } + } + // Primitive types + var object = ''; + if (sub === null) { + if (len === null) + throw new DataError('Invalid tag with undefined length at offset ' + start); + + if (tagClass === 0) { + switch (tagNumber) { + case 0x01: // BOOLEAN + object = buffer[0] !== 0; + break; + case 0x02: // INTEGER + case 0x0a: // ENUMIRATED + if (len > 6) { + object = Int16.encode(buffer); + } else { + var v = buffer[0]; + if (buffer[0] > 0x7f) + v = v - 256; + for (var i = 1; i < len; i++) + v = v * 256 + buffer[i]; + object = v; + } + break; + case 0x03: // BIT_STRING + if (len > 5) { // Content buffer + object = new Uint8Array(buffer.subarray(1)).buffer; + } else { // Max bit mask only for 32 bit + var unusedBit = buffer[0], + skip = unusedBit, s = []; + for (var i = len - 1; i >= 1; --i) { + var b = buffer[i]; + for (var j = skip; j < 8; ++j) + s.push((b >> j) & 1 ? '1' : '0'); + skip = 0; + } + object = s.reverse().join(''); + } + break; + case 0x04: // OCTET_STRING + object = new Uint8Array(buffer).buffer; + break; + // case 0x05: // NULL + case 0x06: // OBJECT_IDENTIFIER + var s = '', + n = 0, + bits = 0; + for (var i = 0; i < len; ++i) { + var v = buffer[i]; + n = (n << 7) + (v & 0x7F); + bits += 7; + if (!(v & 0x80)) { // finished + if (s === '') { + var m = n < 80 ? n < 40 ? 0 : 1 : 2; + s = m + "." + (n - m * 40); + } else + s += "." + n.toString(); + n = 0; + bits = 0; + } + } + if (bits > 0) + throw new DataError('Incompleted OID at offset ' + start); + object = s; + break; + //case 0x07: // ObjectDescriptor + //case 0x08: // EXTERNAL + //case 0x09: // REAL + //case 0x0A: // ENUMERATED + //case 0x0B: // EMBEDDED_PDV + case 0x10: // SEQUENCE + case 0x11: // SET + object = []; + break; + case 0x0C: // UTF8String + object = Chars.encode(buffer, 'utf8'); + break; + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + case 0x19: // GraphicString + case 0x1A: // VisibleString + case 0x1B: // GeneralString + object = Chars.encode(buffer, 'ascii'); + break; + case 0x1C: // UniversalString + object = Chars.encode(buffer, 'utf32'); + break; + case 0x1E: // BMPString + object = Chars.encode(buffer, 'utf16'); + break; + case 0x17: // UTCTime + case 0x18: // GeneralizedTime + var shortYear = tagNumber === 0x17; + var s = Chars.encode(buffer, 'ascii'), + m = (shortYear ? + /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/ : + /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/).exec(s); + if (!m) + throw new DataError('Unrecognized time format "' + s + '" at offset ' + start); + if (shortYear) { + // Where YY is greater than or equal to 50, the year SHALL be interpreted as 19YY; and + // Where YY is less than 50, the year SHALL be interpreted as 20YY + m[1] = +m[1]; + m[1] += (m[1] < 50) ? 2000 : 1900; + } + var dt = new Date(m[1], +m[2] - 1, +m[3], +(m[4] || '0'), +(m[5] || '0'), +(m[6] || '0'), +(m[7] || '0')), + tz = dt.getTimezoneOffset(); + if (m[8] || tagNumber === 0x17) { + if (m[8].toUpperCase() !== 'Z' && m[9]) { + tz = tz + parseInt(m[9]); + } + dt.setMinutes(dt.getMinutes() - tz); + } + dt.original = s; + object = dt; + break; + } + } else // OCTET_STRING + object = new Uint8Array(buffer).buffer; + } else + object = sub; + + // result + return { + tagConstructed: tagConstructed, + tagClass: tagClass, + tagNumber: tagNumber, + header: header, + content: content, + object: object + }; + } + + return { + /** + * BER.decode(object, format) convert javascript object to ASN.1 format CryptoOperationData

+ * If object has members tagNumber, tagClass and tagConstructed + * it is clear define encoding rules. Else method use defaul rules: + * + * SEQUENCE or SET arrays recursively encoded for each item.
+ * OCTET STRING and BIT STRING can presents as array with one item. + * It means encapsulates encoding for child element.
+ * + * If CONTEXT or APPLICATION classes item presents as array with one + * item we use EXPLICIT encoding for element, else IMPLICIT encoding.
+ * + * @memberOf GostCoding.BER + * @param {Object} object Object to encoding + * @param {string} format Encoding rule: 'DER' or 'CER', default 'DER' + * @param {boolean} onlyContent Encode content only, without header + * @returns {CryptoOperationData} BER encoded data + */ + encode: function (object, format, onlyContent) { + return encodeBER(object, format, onlyContent).buffer; + }, + /** + * BER.encode(data) convert ASN.1 format CryptoOperationData data to javascript object

+ * + * Conversion rules to javascript object: + * + * @memberOf GostCoding.BER + * @param {(CryptoOperationData|GostCoding.BER)} data Binary data to decode + * @returns {Object} Javascript object with result of decoding + */ + decode: function (data) { + return decodeBER(data.object ? data : new Uint8Array(buffer(data)), 0); + } + }; //
+ })(); + + /** + * BER, DER, CER conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.BER + */ + GostCoding.prototype.BER = BER; + + /** + * PEM conversion + * @class GostCoding.PEM + */ + var PEM = {// + /** + * PEM.encode(data, name) encode CryptoOperationData to PEM format with name label + * + * @memberOf GostCoding.PEM + * @param {(Object|CryptoOperationData)} data Java script object or BER-encoded binary data + * @param {string} name Name of PEM object: 'certificate', 'private key' etc. + * @returns {string} Encoded object + */ + encode: function (data, name) { + return (name ? '-----BEGIN ' + name.toUpperCase() + '-----\r\n' : '') + + Base64.encode(data instanceof CryptoOperationData ? data : BER.encode(data)) + + (name ? '\r\n-----END ' + name.toUpperCase() + '-----' : ''); + }, + /** + * PEM.decode(s, name, deep) decode PEM format s labeled name to CryptoOperationData or javascript object in according to deep parameter + * + * @memberOf GostCoding.PEM + * @param {string} s PEM encoded string + * @param {string} name Name of PEM object: 'certificate', 'private key' etc. + * @param {boolean} deep If true method do BER-decoding, else only BASE64 decoding + * @param {integer} index Index of decoded value + * @returns {(Object|CryptoOperationData)} Decoded javascript object if deep=true, else CryptoOperationData for father BER decoding + */ + decode: function (s, name, deep, index) { + // Try clear base64 + var re1 = /([A-Za-z0-9\+\/\s\=]+)/g, + valid = re1.exec(s); + if (valid[1].length !== s.length) + valid = false; + if (!valid && name) { + // Try with the name + var re2 = new RegExp( + '-----\\s?BEGIN ' + name.toUpperCase() + + '-----([A-Za-z0-9\\+\\/\\s\\=]+)-----\\s?END ' + + name.toUpperCase() + '-----', 'g'); + valid = re2.exec(s); + } + if (!valid) { + // Try with some name + var re3 = new RegExp( + '-----\\s?BEGIN [A-Z0-9\\s]+' + + '-----([A-Za-z0-9\\+\\/\\s\\=]+)-----\\s?END ' + + '[A-Z0-9\\s]+-----', 'g'); + valid = re3.exec(s); + } + var r = valid && valid[1 + (index || 0)]; + if (!r) + throw new DataError('Not valid PEM format'); + var out = Base64.decode(r); + if (deep) + out = BER.decode(out); + return out; + } // + }; + + /** + * PEM conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.PEM + */ + GostCoding.prototype.PEM = PEM; + + if (gostCrypto) + /** + * Coding algorithms: Base64, Hex, Int16, Chars, BER and PEM + * + * @memberOf gostCrypto + * @type GostCoding + */ + gostCrypto.coding = new GostCoding(); + + return GostCoding; + +})); \ No newline at end of file diff --git a/src/core/vendor/streebog/gostCrypto.js b/src/core/vendor/streebog/gostCrypto.js new file mode 100644 index 00000000..fd6b19a2 --- /dev/null +++ b/src/core/vendor/streebog/gostCrypto.js @@ -0,0 +1,1657 @@ +/** + * @file Implementation Web Crypto interfaces for GOST algorithms + * @version 1.76 + * @copyright 2014-2016, Rudolf Nickolaev. All rights reserved. + */ + +/* + * 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. + * + * 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. + * + */ + +(function (root, factory) { + + /* + * Module imports and exports + * + */ // + module.exports = factory(require('./gostRandom')); + + // + +}(this, function (GostRandom) { + + /* + * Algorithm normalization + * + */ // + + var root = this; + var rootCrypto = root.crypto || root.msCrypto; + + var SyntaxError = root.SyntaxError || root.Error, + DataError = root.DataError || root.Error, + NotSupportedError = root.NotSupportedError || root.Error, + OperationError = root.OperationError || root.Error, + InvalidStateError = root.InvalidAccessError || root.Error, + InvalidAccessError = root.InvalidAccessError || root.Error; + + // Normalize algorithm + function normalize(algorithm, method) { + if (typeof algorithm === 'string' || algorithm instanceof String) + algorithm = {name: algorithm}; + var name = algorithm.name; + if (!name) + throw new SyntaxError('Algorithm name not defined'); + // Extract algorithm modes from name + var modes = name.split('/'), modes = modes[0].split('-').concat(modes.slice(1)); + // Normalize the name with default modes + var na = {}; + name = modes[0].replace(/[\.\s]/g, ''); + modes = modes.slice(1); + if (name.indexOf('28147') >= 0) { + na = { + name: 'GOST 28147', + version: 1989, + mode: (algorithm.mode || (// ES, MAC, KW + (method === 'sign' || method === 'verify') ? 'MAC' : + (method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(), + length: algorithm.length || 64 + }; + } else if (name.indexOf('3412') >= 0) { + na = { + name: 'GOST R 34.12', + version: 2015, + mode: (algorithm.mode || (// ES, MAC, KW + (method === 'sign' || method === 'verify') ? 'MAC' : + (method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(), + length: algorithm.length || 64 // 128 + }; + } else if (name.indexOf('3411') >= 0) { + na = { + name: 'GOST R 34.11', + version: 2012, // 1994 + mode: (algorithm.mode || (// HASH, KDF, HMAC, PBKDF2, PFXKDF, CPKDF + (method === 'deriveKey' || method === 'deriveBits') ? 'KDF' : + (method === 'sign' || method === 'verify') ? 'HMAC' : 'HASH')).toUpperCase(), + length: algorithm.length || 256 // 512 + }; + } else if (name.indexOf('3410') >= 0) { + na = { + name: 'GOST R 34.10', + version: 2012, // 1994, 2001 + mode: (algorithm.mode || (// SIGN, DH, MASK + (method === 'deriveKey' || method === 'deriveBits') ? 'DH' : 'SIGN')).toUpperCase(), + length: algorithm.length || 256 // 512 + }; + } else if (name.indexOf('SHA') >= 0) { + na = { + name: 'SHA', + version: (algorithm.length || 160) === 160 ? 1 : 2, // 1, 2 + mode: (algorithm.mode || (// HASH, KDF, HMAC, PBKDF2, PFXKDF + (method === 'deriveKey' || method === 'deriveBits') ? 'KDF' : + (method === 'sign' || method === 'verify') ? 'HMAC' : 'HASH')).toUpperCase(), + length: algorithm.length || 160 + }; + } else if (name.indexOf('RC2') >= 0) { + na = { + name: 'RC2', + version: 1, + mode: (algorithm.mode || (// ES, MAC, KW + (method === 'sign' || method === 'verify') ? 'MAC' : + (method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(), + length: algorithm.length || 32 // 1 - 1024 + }; + } else if (name.indexOf('PBKDF2') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'PBKDF2'; + } else if (name.indexOf('PFXKDF') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'PFXKDF'; + } else if (name.indexOf('CPKDF') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'CPKDF'; + } else if (name.indexOf('HMAC') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'HMAC'; + } else + throw new NotSupportedError('Algorithm not supported'); + + // Compile modes + modes.forEach(function (mode) { + mode = mode.toUpperCase(); + if (/^[0-9]+$/.test(mode)) { + if ((['8', '16', '32'].indexOf(mode) >= 0) || (na.length === '128' && mode === '64')) { // Shift bits + if (na.mode === 'ES') + na.shiftBits = parseInt(mode); + else if (na.mode === 'MAC') + na.macLength = parseInt(mode); + else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + } else if (['89', '94', '01', '12', '15', '1989', '1994', '2001', '2012', '2015'].indexOf(mode) >= 0) { // GOST Year + var version = parseInt(mode); + version = version < 1900 ? (version < 80 ? 2000 + version : 1900 + version) : version; + na.version = version; + } else if (['1'].indexOf(mode) >= 0 && na.name === 'SHA') { // SHA-1 + na.version = 1; + na.length = 160; + } else if (['256', '384', '512'].indexOf(mode) >= 0 && na.name === 'SHA') { // SHA-2 + na.version = 2; + na.length = parseInt(mode); + } else if (['40', '128'].indexOf(mode) >= 0 && na.name === 'RC2') { // RC2 + na.version = 1; + na.length = parseInt(mode); // key size + } else if (['64', '128', '256', '512'].indexOf(mode) >= 0) // block size + na.length = parseInt(mode); + else if (['1000', '2000'].indexOf(mode) >= 0) // Iterations + na.iterations = parseInt(mode); + // Named Paramsets + } else if (['E-TEST', 'E-A', 'E-B', 'E-C', 'E-D', 'E-SC', 'E-Z', 'D-TEST', 'D-A', 'D-SC'].indexOf(mode) >= 0) { + na.sBox = mode; + } else if (['S-TEST', 'S-A', 'S-B', 'S-C', 'S-D', 'X-A', 'X-B', 'X-C'].indexOf(mode) >= 0) { + na.namedParam = mode; + } else if (['S-256-TEST', 'S-256-A', 'S-256-B', 'S-256-C', 'P-256', 'T-512-TEST', 'T-512-A', + 'T-512-B', 'X-256-A', 'X-256-B', 'T-256-TEST', 'T-256-A', 'T-256-B', 'S-256-B', 'T-256-C', 'S-256-C'].indexOf(mode) >= 0) { + na.namedCurve = mode; + } else if (['SC', 'CP', 'VN'].indexOf(mode) >= 0) { + na.procreator = mode; + + // Encription GOST 28147 or GOST R 34.12 + } else if (na.name === 'GOST 28147' || na.name === 'GOST R 34.12' || na.name === 'RC2') { + if (['ES', 'MAC', 'KW', 'MASK'].indexOf(mode) >= 0) { + na.mode = mode; + } else if (['ECB', 'CFB', 'OFB', 'CTR', 'CBC'].indexOf(mode) >= 0) { + na.mode = 'ES'; + na.block = mode; + } else if (['CPKW', 'NOKW', 'SCKW'].indexOf(mode) >= 0) { + na.mode = 'KW'; + na.keyWrapping = mode.replace('KW', ''); + } else if (['ZEROPADDING', 'PKCS5PADDING', 'NOPADDING', 'RANDOMPADDING', 'BITPADDING'].indexOf(mode) >= 0) { + na.padding = mode.replace('PADDING', ''); + } else if (['NOKM', 'CPKM'].indexOf(mode) >= 0) { + na.keyMeshing = mode.replace('KM', ''); + } else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + + // Digesting GOST 34.11 + } else if (na.name === 'GOST R 34.11' || na.name === 'SHA') { + if (['HASH', 'KDF', 'HMAC', 'PBKDF2', 'PFXKDF', 'CPKDF'].indexOf(mode) >= 0) + na.mode = mode; + else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + + // Signing GOST 34.10 + } else if (na.name === 'GOST R 34.10') { + var hash = mode.replace(/[\.\s]/g, ''); + if (hash.indexOf('GOST') >= 0 && hash.indexOf('3411') >= 0) + na.hash = mode; + else if (['SIGN', 'DH', 'MASK'].indexOf(mode)) + na.mode = mode; + else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + } + }); + + // Procreator + na.procreator = algorithm.procreator || na.procreator || 'CP'; + + // Key size + switch (na.name) { + case 'GOST R 34.10': + na.keySize = na.length / (na.version === 1994 ? 4 : 8); + break; + case 'GOST R 34.11': + na.keySize = 32; + break; + case 'GOST 28147': + case 'GOST R 34.12': + na.keySize = 32; + break; + case 'RC2': + na.keySize = Math.ceil(na.length / 8); + break; + case 'SHA': + na.keySize = na.length / 8; + break; + } + + // Encrypt additional modes + if (na.mode === 'ES') { + if (algorithm.block) + na.block = algorithm.block; // ECB, CFB, OFB, CTR, CBC + if (na.block) + na.block = na.block.toUpperCase(); + if (algorithm.padding) + na.padding = algorithm.padding; // NO, ZERO, PKCS5, RANDOM, BIT + if (na.padding) + na.padding = na.padding.toUpperCase(); + if (algorithm.shiftBits) + na.shiftBits = algorithm.shiftBits; // 8, 16, 32, 64 + if (algorithm.keyMeshing) + na.keyMeshing = algorithm.keyMeshing; // NO, CP + if (na.keyMeshing) + na.keyMeshing = na.keyMeshing.toUpperCase(); + // Default values + if (method !== 'importKey' && method !== 'generateKey') { + na.block = na.block || 'ECB'; + na.padding = na.padding || (na.block === 'CBC' || na.block === 'ECB' ? 'ZERO' : 'NO'); + if (na.block === 'CFB' || na.block === 'OFB') + na.shiftBits = na.shiftBits || na.length; + na.keyMeshing = na.keyMeshing || 'NO'; + } + } + if (na.mode === 'KW') { + if (algorithm.keyWrapping) + na.keyWrapping = algorithm.keyWrapping; // NO, CP, SC + if (na.keyWrapping) + na.keyWrapping = na.keyWrapping.toUpperCase(); + if (method !== 'importKey' && method !== 'generateKey') + na.keyWrapping = na.keyWrapping || 'NO'; + } + + // Paramsets + ['sBox', 'namedParam', 'namedCurve', 'curve', 'param', 'modulusLength'].forEach(function (name) { + algorithm[name] && (na[name] = algorithm[name]); + }); + // Default values + if (method !== 'importKey' && method !== 'generateKey') { + if (na.name === 'GOST 28147') { + na.sBox = na.sBox || (na.procreator === 'SC' ? 'E-SC' : 'E-A'); // 'E-A', 'E-B', 'E-C', 'E-D', 'E-SC' + } else if (na.name === 'GOST R 34.12' && na.length === 64) { + na.sBox = 'E-Z'; + } else if (na.name === 'GOST R 34.11' && na.version === 1994) { + na.sBox = na.sBox || (na.procreator === 'SC' ? 'D-SC' : 'D-A'); // 'D-SC' + } else if (na.name === 'GOST R 34.10' && na.version === 1994) { + na.namedParam = na.namedParam || (na.mode === 'DH' ? 'X-A' : 'S-A'); // 'S-B', 'S-C', 'S-D', 'X-B', 'X-C' + } else if (na.name === 'GOST R 34.10' && na.version === 2001) { + na.namedCurve = na.namedCurve || (na.length === 256 ? + na.procreator === 'SC' ? 'P-256' : (na.mode === 'DH' ? 'X-256-A' : 'S-256-A') : // 'S-256-B', 'S-256-C', 'X-256-B', 'T-256-A', 'T-256-B', 'T-256-C', 'P-256' + na.mode === 'T-512-A'); // 'T-512-B', 'T-512-C' + } else if (na.name === 'GOST R 34.10' && na.version === 2012) { + na.namedCurve = na.namedCurve || (na.length === 256 ? + na.procreator === 'SC' ? 'P-256' : (na.mode === 'DH' ? 'X-256-A' : 'S-256-A') : // 'S-256-B', 'S-256-C', 'X-256-B', 'T-256-A', 'T-256-B', 'T-256-C', 'P-256' + na.mode === 'T-512-A'); // 'T-512-B', 'T-512-C' + } + } + + // Vectors + switch (na.mode) { + case 'DH': + algorithm.ukm && (na.ukm = algorithm.ukm); + algorithm['public'] && (na['public'] = algorithm['public']); + break; + case 'SIGN': + case 'KW': + algorithm.ukm && (na.ukm = algorithm.ukm); + break; + case 'ES': + case 'MAC': + algorithm.iv && (na.iv = algorithm.iv); + break; + case 'KDF': + algorithm.label && (na.label = algorithm.label); + algorithm.contex && (na.context = algorithm.contex); + break; + case 'PBKDF2': + algorithm.salt && (na.salt = algorithm.salt); + algorithm.iterations && (na.iterations = algorithm.iterations); + algorithm.diversifier && (na.diversifier = algorithm.diversifier); + break; + case 'PFXKDF': + algorithm.salt && (na.salt = algorithm.salt); + algorithm.iterations && (na.iterations = algorithm.iterations); + algorithm.diversifier && (na.diversifier = algorithm.diversifier); + break; + case 'CPKDF': + algorithm.salt && (na.salt = algorithm.salt); + algorithm.iterations && (na.iterations = algorithm.iterations); + break; + } + + // Verification method and modes + if (method && ( + ((na.mode !== 'ES' && na.mode !== 'SIGN' && na.mode !== 'MAC' && + na.mode !== 'HMAC' && na.mode !== 'KW' && na.mode !== 'DH' + && na.mode !== 'MASK') && + (method === 'generateKey')) || + ((na.mode !== 'ES') && + (method === 'encrypt' || method === 'decrypt')) || + ((na.mode !== 'SIGN' && na.mode !== 'MAC' && na.mode !== 'HMAC') && + (method === 'sign' || method === 'verify')) || + ((na.mode !== 'HASH') && + (method === 'digest')) || + ((na.mode !== 'KW' && na.mode !== 'MASK') && + (method === 'wrapKey' || method === 'unwrapKey')) || + ((na.mode !== 'DH' && na.mode !== 'PBKDF2' && na.mode !== 'PFXKDF' && + na.mode !== 'CPKDF' && na.mode !== 'KDF') && + (method === 'deriveKey' || method === 'deriveBits')))) + throw new NotSupportedError('Algorithm mode ' + na.mode + ' not valid for method ' + method); + + // Normalize hash algorithm + algorithm.hash && (na.hash = algorithm.hash); + if (na.hash) { + if ((typeof na.hash === 'string' || na.hash instanceof String) + && na.procreator) + na.hash = na.hash + '/' + na.procreator; + na.hash = normalize(na.hash, 'digest'); + } + + // Algorithm object identirifer + algorithm.id && (na.id = algorithm.id); + + return na; + } + + // Check for possibility use native crypto.subtle + function checkNative(algorithm) { + if (!rootCrypto || !rootCrypto.subtle || !algorithm) + return false; + // Prepare name + var name = (typeof algorithm === 'string' || algorithm instanceof String) ? + name = algorithm : algorithm.name; + if (!name) + return false; + name = name.toUpperCase(); + // Digest algorithm for key derivation + if ((name.indexOf('KDF') >= 0 || name.indexOf('HMAC') >= 0) && algorithm.hash) + return checkNative(algorithm.hash); + // True if no supported names + return name.indexOf('GOST') === -1 && + name.indexOf('SHA-1') === -1 && + name.indexOf('RC2') === -1 && + name.indexOf('?DES') === -1; + } + // + + /* + * Key conversion methods + * + */ // + + // Check key parameter + function checkKey(key, method) { + if (!key.algorithm) + throw new SyntaxError('Key algorithm not defined'); + + if (!key.algorithm.name) + throw new SyntaxError('Key algorithm name not defined'); + + var name = key.algorithm.name, + gostCipher = name === 'GOST 28147' || name === 'GOST R 34.12' || name === 'RC2', + gostDigest = name === 'GOST R 34.11' || name === 'SHA', + gostSign = name === 'GOST R 34.10'; + + if (!gostCipher && !gostSign && !gostDigest) + throw new NotSupportedError('Key algorithm ' + name + ' is unsupproted'); + + if (!key.type) + throw new SyntaxError('Key type not defined'); + + if (((gostCipher || gostDigest) && key.type !== 'secret') || + (gostSign && !(key.type === 'public' || key.type === 'private'))) + throw new DataError('Key type ' + key.type + ' is not valid for algorithm ' + name); + + if (!key.usages || !key.usages.indexOf) + throw new SyntaxError('Key usages not defined'); + + for (var i = 0, n = key.usages.length; i < n; i++) { + var md = key.usages[i]; + if (((md === 'encrypt' || md === 'decrypt') && key.type !== 'secret') || + (md === 'sign' && key.type === 'public') || + (md === 'verify' && key.type === 'private')) + throw new InvalidStateError('Key type ' + key.type + ' is not valid for ' + md); + } + + if (method) + if (key.usages.indexOf(method) === -1) + throw new InvalidAccessError('Key usages is not contain method ' + method); + + if (!key.buffer) + throw new SyntaxError('Key buffer is not defined'); + + var size = key.buffer.byteLength * 8, keySize = 8 * key.algorithm.keySize; + if ((key.type === 'secret' && size !== (keySize || 256) && + (key.usages.indexOf('encrypt') >= 0 || key.usages.indexOf('decrypt') >= 0)) || + (key.type === 'private' && !(size === 256 || size === 512)) || + (key.type === 'public' && !(size === 512 || size === 1024))) + throw new SyntaxError('Key buffer has wrong size ' + size + ' bit'); + } + + // Extract key and enrich cipher algorithm + function extractKey(method, algorithm, key) { + checkKey(key, method); + if (algorithm) { + var params; + switch (algorithm.mode) { + case 'ES': + params = ['sBox', 'keyMeshing', 'padding', 'block']; + break; + case 'SIGN': + params = ['namedCurve', 'namedParam', 'sBox', 'curve', 'param', 'modulusLength']; + break; + case 'MAC': + params = ['sBox']; + break; + case 'KW': + params = ['keyWrapping', 'ukm']; + break; + case 'DH': + params = ['namedCurve', 'namedParam', 'sBox', 'ukm', 'curve', 'param', 'modulusLength']; + break; + case 'KDF': + params = ['context', 'label']; + break; + case 'PBKDF2': + params = ['sBox', 'iterations', 'salt']; + break; + case 'PFXKDF': + params = ['sBox', 'iterations', 'salt', 'diversifier']; + break; + case 'CPKDF': + params = ['sBox', 'salt']; + break; + } + if (params) + params.forEach(function (name) { + key.algorithm[name] && (algorithm[name] = key.algorithm[name]); + }); + } + return key.buffer; + } + + // Make key definition + function convertKey(algorithm, extractable, keyUsages, keyData, keyType) { + var key = { + type: keyType || (algorithm.name === 'GOST R 34.10' ? 'private' : 'secret'), + extractable: extractable || 'false', + algorithm: algorithm, + usages: keyUsages || [], + buffer: keyData + }; + checkKey(key); + return key; + } + + function convertKeyPair(publicAlgorithm, privateAlgorithm, extractable, keyUsages, publicBuffer, privateBuffer) { + + if (!keyUsages || !keyUsages.indexOf) + throw new SyntaxError('Key usages not defined'); + + var publicUsages = keyUsages.filter(function (value) { + return value !== 'sign'; + }); + var privateUsages = keyUsages.filter(function (value) { + return value !== 'verify'; + }); + + return { + publicKey: convertKey(publicAlgorithm, extractable, publicUsages, publicBuffer, 'public'), + privateKey: convertKey(privateAlgorithm, extractable, privateUsages, privateBuffer, 'private') + }; + } + + // Swap bytes in buffer + function swapBytes(src) { + if (src instanceof CryptoOperationData) + src = new Uint8Array(src); + var dst = new Uint8Array(src.length); + for (var i = 0, n = src.length; i < n; i++) + dst[n - i - 1] = src[i]; + return dst.buffer; + } + // + + /** + * Promise stub object (not fulfill specification, only for internal use) + * Class not defined if Promise class already defined in root context

+ * + * The Promise object is used for deferred and asynchronous computations. A Promise is in one of the three states: + * + * Another term describing the state is settled: the Promise is either fulfilled or rejected, but not pending.

+ * @class Promise + * @global + * @param {function} executor Function object with two arguments resolve and reject. + * The first argument fulfills the promise, the second argument rejects it. + * We can call these functions, once our operation is completed. + */ // + if (!root.Promise) { + + root.Promise = (function () { + + function mswrap(value) { + if (value && value.oncomplete === null && value.onerror === null) { + return new Promise(function (resolve, reject) { + value.oncomplete = function () { + resolve(value.result); + }; + value.onerror = function () { + reject(new OperationError(value.toString())); + }; + }); + } else + return value; + } + + function Promise(executor) { + + var state = 'pending', result, + resolveQueue = [], rejectQueue = []; + + function call(callback) { + try { + callback(); + } catch (e) { + } + } + + try { + executor(function (value) { + if (state === 'pending') { + state = 'fulfilled'; + result = value; + resolveQueue.forEach(call); + } + }, function (reason) { + if (state === 'pending') { + state = 'rejected'; + result = reason; + rejectQueue.forEach(call); + } + }); + } catch (error) { + if (state === 'pending') { + state = 'rejected'; + result = error; + rejectQueue.forEach(call); + } + } + /** + * The then() method returns a Promise. It takes two arguments, both are + * callback functions for the success and failure cases of the Promise. + * + * @method then + * @memberOf Promise + * @instance + * @param {function} onFulfilled A Function called when the Promise is fulfilled. This function has one argument, the fulfillment value. + * @param {function} onRejected A Function called when the Promise is rejected. This function has one argument, the rejection reason. + * @returns {Promise} + */ + this.then = function (onFulfilled, onRejected) { + + return new Promise(function (resolve, reject) { + + function asyncOnFulfilled() { + var value; + try { + value = onFulfilled ? onFulfilled(result) : result; + } catch (error) { + reject(error); + return; + } + value = mswrap(value); + if (value && value.then && value.then.call) { + value.then(resolve, reject); + } else { + resolve(value); + } + } + + function asyncOnRejected() { + var reason; + try { + reason = onRejected ? onRejected(result) : result; + } catch (error) { + reject(error); + return; + } + reason = mswrap(reason); + if (reason && reason.then && reason.then.call) { + reason.then(resolve, reject); + } else { + reject(reason); + } + } + + if (state === 'fulfilled') { + asyncOnFulfilled(); + } else if (state === 'rejected') { + asyncOnRejected(); + } else { + resolveQueue.push(asyncOnFulfilled); + rejectQueue.push(asyncOnRejected); + } + + }); + + }; + /** + * The catch() method returns a Promise and deals with rejected cases only. + * It behaves the same as calling Promise.prototype.then(undefined, onRejected). + * + * @method catch + * @memberOf Promise + * @instance + * @param {function} onRejected A Function called when the Promise is rejected. This function has one argument, the rejection reason. + * @returns {Promise} + */ + this['catch'] = function (onRejected) { + return this.then(undefined, onRejected); + }; + } + + /** + * The Promise.all(iterable) method returns a promise that resolves when all + * of the promises in the iterable argument have resolved.

+ * + * The result is passed as an array of values from all the promises. + * If something passed in the iterable array is not a promise, it's converted to + * one by Promise.resolve. If any of the passed in promises rejects, the + * all Promise immediately rejects with the value of the promise that rejected, + * discarding all the other promises whether or not they have resolved. + * + * @method all + * @memberOf Promise + * @static + * @param {KeyUsages} promises Array with promises. + * @returns {Promise} + */ + Promise.all = function (promises) { + return new Promise(function (resolve, reject) { + var result = [], count = 0; + function asyncResolve(k) { + count++; + return function (data) { + result[k] = data; + count--; + if (count === 0) + resolve(result); + }; + } + + function asyncReject(reason) { + if (count > 0) + reject(reason); + count = 0; + } + + for (var i = 0, n = promises.length; i < n; i++) { + var data = promises[i]; + if (data.then && data.then.call) + data.then(asyncResolve(i), asyncReject); + else + result[i] = data; + } + + if (count === 0) + resolve(result); + }); + }; + + return Promise; + })(); + } //
+ + /* + * Worker executor + * + */ // + + var baseUrl = '', nameSuffix = ''; + // Try to define from DOM model + if (typeof document !== 'undefined') { + (function () { + var regs = /^(.*)gostCrypto(.*)\.js$/i; + var list = document.querySelectorAll('script'); + for (var i = 0, n = list.length; i < n; i++) { + var value = list[i].getAttribute('src'); + var test = regs.exec(value); + if (test) { + baseUrl = test[1]; + nameSuffix = test[2]; + } + } + })(); + } + + // Local importScripts procedure for include dependens + function importScripts() { + for (var i = 0, n = arguments.length; i < n; i++) { + var name = arguments[i].split('.'), + src = baseUrl + name[0] + nameSuffix + '.' + name[1]; + var el = document.querySelector('script[src="' + src + '"]'); + if (!el) { + el = document.createElement('script'); + el.setAttribute('src', src); + document.head.appendChild(el); + } + } + } + + // Create Worker + var worker = false, tasks = [], sequence = 0; + // Worker will create only for first child process and + // Gost implementation libraries not yet loaded + if (!root.importScripts && !root.gostEngine) { + + try { + worker = new Worker(baseUrl + 'gostEngine' + nameSuffix + '.js'); + + // Result of opertion + worker.onmessage = function (event) { + // Find task + var id = event.data.id; + for (var i = 0, n = tasks.length; i < n; i++) + if (tasks[i].id === id) + break; + if (i < n) { + var task = tasks[i]; + tasks.splice(i, 1); + // Reject if error or resolve with result + if (event.data.error) + task.reject(new OperationError(event.data.error)); + else + task.resolve(event.data.result); + } + }; + + // Worker error - reject all waiting tasks + worker.onerror = function (event) { + for (var i = 0, n = tasks.length; i < n; i++) + tasks[i].reject(event.error); + tasks = []; + }; + + } catch (e) { + // Worker is't supported + worker = false; + } + } + + if (!root.importScripts) { + // This procedure emulate load dependents as in Worker + root.importScripts = importScripts; + + } + + if (!worker) { + // Import main module + // Reason: we are already in worker process or Worker interface is not + // yet supported + root.gostEngine //|| importScripts('gostEngine.js'); + } + + // Executor for any method + function execute(algorithm, method, args) { + return new Promise(function (resolve, reject) { + try { + if (worker) { + var id = ++sequence; + tasks.push({ + id: id, + resolve: resolve, + reject: reject + }); + worker.postMessage({ + id: id, algorithm: algorithm, + method: method, args: args + }); + } else { + if (root.gostEngine) + resolve(root.gostEngine.execute(algorithm, method, args)); + else + reject(new OperationError('Module gostEngine not found')); + } + } catch (error) { + reject(error); + } + }); + } + + // Self resolver + function call(callback) { + try { + callback(); + } catch (e) { + } + } + + // + + /* + * WebCrypto common class references + * + */ // + /** + * The Algorithm object is a dictionary object [WebIDL] which is used to + * specify an algorithm and any additional parameters required to fully + * specify the desired operation.
+ *
+     *  dictionary Algorithm {
+     *      DOMString name;
+     *  };
+     * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary} + * @class Algorithm + * @param {DOMString} name The name of the registered algorithm to use. + */ + + /** + * AlgorithmIdentifier - Algorithm or DOMString name of algorithm
+ *
+     *  typedef (Algorithm or DOMString) AlgorithmIdentifier;
+     * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary} + * @class AlgorithmIdentifier + */ + + /** + * The KeyAlgorithm interface represents information about the contents of a + * given Key object. + *
+     *  interface KeyAlgorithm {
+     *      readonly attribute DOMString name
+     *  };
+     * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-algorithm-interface} + * @class KeyAlgorithm + * @param {DOMString} name The name of the algorithm used to generate the Key + */ + + /** + * The type of a key. The recognized key type values are "public", "private" + * and "secret". Opaque keying material, including that used for symmetric + * algorithms, is represented by "secret", while keys used as part of asymmetric + * algorithms composed of public/private keypairs will be either "public" or "private". + *
+     *  typedef DOMString KeyType;
+     * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class KeyType + */ + + /** + * Sequence of operation type that may be performed using a key. The recognized + * key usage values are "encrypt", "decrypt", "sign", "verify", "deriveKey", + * "deriveBits", "wrapKey" and "unwrapKey". + *
+     *  typedef DOMString[] KeyUsages;
+     * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class KeyUsages + */ + + /** + * The Key object represents an opaque reference to keying material that is + * managed by the user agent.
+ * This specification provides a uniform interface for many different kinds of + * keying material managed by the user agent. This may include keys that have + * been generated by the user agent, derived from other keys by the user agent, + * imported to the user agent through user actions or using this API, + * pre-provisioned within software or hardware to which the user agent has + * access or made available to the user agent in other ways. The term key refers + * broadly to any keying material including actual keys for cryptographic + * operations and secret values obtained within key derivation or exchange operations.
+ * The Key object is not required to directly interface with the underlying key + * storage mechanism, and may instead simply be a reference for the user agent + * to understand how to obtain the keying material when needed, eg. when performing + * a cryptographic operation. + *
+     *  interface Key {
+     *      readonly attribute KeyType type;
+     *      readonly attribute boolean extractable;
+     *      readonly attribute KeyAlgorithm algorithm;
+     *      readonly attribute KeyUsages usages;
+     *  };     
+     * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class Key + * @param {KeyType} type The type of a key. The recognized key type values are "public", "private" and "secret". + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application. + * @param {KeyAlgorithm} algorithm The Algorithm used to generate the key. + * @param {KeyUsages} usages Key usage array: type of operation that may be performed using a key. + */ + + /** + * The KeyPair interface represents an asymmetric key pair that is comprised of both public and private keys. + *
+     *  interface KeyPair {
+     *      readonly attribute Key publicKey;
+     *      readonly attribute Key privateKey;
+     *  };     
+     * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#keypair} + * @class KeyPair + * @param {Key} privateKey Private key + * @param {Key} publicKey Public key + */ + + /** + * Specifies a serialization format for a key. The recognized key format values are: + * + *
+     *  typedef DOMString KeyFormat;
+     *  
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class KeyFormat + */ + + /** + * Binary data + *
+     *  typedef (ArrayBuffer or ArrayBufferView) CryptoOperationData;
+     *  
+ * @class CryptoOperationData + */ + var CryptoOperationData = root.ArrayBuffer; + + /** + * DER-encoded ArrayBuffer or PEM-encoded DOMString constains ASN.1 object
+ *
+     *  typedef (ArrayBuffer or DOMString) FormatedData;
+     * 
+ * @class FormatedData + */ + //
+ + /** + * The gostCrypto provide general purpose cryptographic functionality for + * GOST standards including a cryptographically strong pseudo-random number + * generator seeded with truly random values. + * + * @namespace gostCrypto + */ + var gostCrypto = {}; + + /** + * The SubtleCrypto class provides low-level cryptographic primitives and algorithms. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface} + * + * @class SubtleCrypto + */ // + function SubtleCrypto() { + } + + /** + * The encrypt method returns a new Promise object that will encrypt data + * using the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-encrypt}

+ * + * Supported algorithm names: + * + * For more information see {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method encrypt + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ + SubtleCrypto.prototype.encrypt = function (algorithm, key, data) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.encrypt(algorithm, key, data); + + algorithm = normalize(algorithm, 'encrypt'); + return execute(algorithm, 'encrypt', + [extractKey('encrypt', algorithm, key), data]); + }); + }; // + + /** + * The decrypt method returns a new Promise object that will decrypt data + * using the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-decrypt}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method decrypt + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ + SubtleCrypto.prototype.decrypt = function (algorithm, key, data) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.decrypt(algorithm, key, data); + + algorithm = normalize(algorithm, 'decrypt'); + return execute(algorithm, 'decrypt', + [extractKey('decrypt', algorithm, key), data]); + }); + }; // + + /** + * The sign method returns a new Promise object that will sign data using + * the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-sign}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method sign + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ + SubtleCrypto.prototype.sign = function (algorithm, key, data) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.sign(algorithm, key, data); + + algorithm = normalize(algorithm, 'sign'); + var value = execute(algorithm, 'sign', + [extractKey('sign', algorithm, key), data]).then(function (data) { + if (algorithm.procreator === 'SC' && algorithm.mode === 'SIGN') { + data = gostCrypto.asn1.GostSignature.encode(data); + } + return data; + }); + return value; + }); + }; // + + /** + * The verify method returns a new Promise object that will verify data + * using the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-verify}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method verify + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} signature Signature data + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with boolean value of verification result + */ + SubtleCrypto.prototype.verify = function (algorithm, key, signature, data) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.verify(algorithm, key, signature, data); + + algorithm = normalize(algorithm, 'verify'); + if (algorithm.procreator === 'SC' && algorithm.mode === 'SIGN') { + var obj = gostCrypto.asn1.GostSignature.decode(signature); + signature = {r: obj.r, s: obj.s}; + } + return execute(algorithm, 'verify', + [extractKey('verify', algorithm, key), signature, data]); + }); + }; // + + /** + * The digest method returns a new Promise object that will digest data + * using the specified algorithm identifier. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-digest}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostDigest} + * + * @memberOf SubtleCrypto + * @method digest + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ + SubtleCrypto.prototype.digest = function (algorithm, data) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.digest(algorithm, data); + + algorithm = normalize(algorithm, 'digest'); + return execute(algorithm, 'digest', [data]); + }); + }; // + + /** + * The generateKey method returns a new Promise object that will key(s) using + * the specified algorithm identifier. Key can be used in according with + * KeyUsages sequence. The recognized key usage values are "encrypt", "decrypt", + * "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey". + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-generateKey}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
+ * Note: Generation key for GOST R 34.10-94 not supported. + * + * @memberOf SubtleCrypto + * @method generateKey + * @instance + * @param {AlgorithmIdentifier} algorithm Key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} or {@link KeyPair} in according to key algorithm + */ + SubtleCrypto.prototype.generateKey = function (algorithm, extractable, keyUsages) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.generateKey(algorithm, extractable, keyUsages); + + var privateAlgorithm = algorithm.privateKey, + publicAlgorithm = algorithm.publicKey; + algorithm = normalize(algorithm, 'generateKey'); + if (privateAlgorithm) + privateAlgorithm = normalize(privateAlgorithm, 'generateKey'); + else + privateAlgorithm = algorithm; + if (publicAlgorithm) + publicAlgorithm = normalize(publicAlgorithm, 'generateKey'); + else + publicAlgorithm = algorithm; + return execute(algorithm, 'generateKey', []).then(function (data) { + if (data.publicKey && data.privateKey) + return convertKeyPair(publicAlgorithm, privateAlgorithm, extractable, keyUsages, data.publicKey, data.privateKey); + else + return convertKey(algorithm, extractable, keyUsages, data); + }); + }); + }; // + + /** + * The deriveKey method returns a new Promise object that will key(s) using + * the specified algorithm identifier. Key can be used in according with + * KeyUsage sequence. The recognized key usage values are "encrypt", "decrypt", + * "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey". + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-deriveKey}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostSign} and {@link GostDigest} + * + * @memberOf SubtleCrypto + * @method deriveKey + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} baseKey Derivation key object + * @param {AlgorithmIdentifier} derivedKeyType Derived key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} + */ + SubtleCrypto.prototype.deriveKey = function (algorithm, baseKey, + derivedKeyType, extractable, keyUsages) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.deriveKey(algorithm, baseKey, + derivedKeyType, extractable, keyUsages); + + algorithm = normalize(algorithm, 'deriveKey'); + derivedKeyType = normalize(derivedKeyType, 'generateKey'); + algorithm.keySize = derivedKeyType.keySize; + if (algorithm['public']) { + algorithm['public'].algorithm = normalize(algorithm['public'].algorithm); + algorithm['public'] = extractKey('deriveKey', algorithm, algorithm['public']); + } + return execute(algorithm, 'deriveKey', [extractKey('deriveKey', algorithm, baseKey)]).then(function (data) { + return convertKey(derivedKeyType, extractable, keyUsages, data); + }); + }); + }; // + + /** + * The deriveBits method returns length bits on baseKey using the + * specified algorithm identifier. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-deriveBits}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostSign} and {@link GostDigest} + * + * @memberOf SubtleCrypto + * @method deriveBits + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} baseKey Derivation key object + * @param {number} length Length bits + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ + SubtleCrypto.prototype.deriveBits = function (algorithm, baseKey, length) // + { + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.deriveBits(algorithm, baseKey, length); + + algorithm = normalize(algorithm, 'deriveBits'); + if (algorithm['public']) + algorithm['public'] = extractKey('deriveBits', algorithm, algorithm['public']); + return execute(algorithm, 'deriveBits', [extractKey('deriveBits', algorithm, baseKey), length]); + }); + }; // + + /** + * The importKey method returns a new Promise object that will key(s) using + * the specified algorithm identifier. Key can be used in according with + * KeyUsage sequence. The recognized key usage values are "encrypt", "decrypt", + * "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey".

+ * Parameter keyData contains data in defined format. + * The suppored key format values are: + * + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method importKey + * @instance + * @param {KeyFormat} format Key format Format specifies a serialization format for a key + * @param {CryptoOperationData} keyData + * @param {AlgorithmIdentifier} algorithm Key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} + */ + SubtleCrypto.prototype.importKey = function (format, keyData, algorithm, extractable, keyUsages) // + { + var type; + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages); + + if (format === 'raw') { + algorithm = normalize(algorithm, 'importKey'); + if (keyUsages && keyUsages.indexOf) { + var name = algorithm.name.toUpperCase().replace(/[\.\s]/g, ''); + if (name.indexOf('3410') >= 0 && keyUsages.indexOf('sign') >= 0) + type = 'private'; + else if (name.indexOf('3410') >= 0 && keyUsages.indexOf('verify') >= 0) + type = 'public'; + } + return keyData; + } else { + var key; + if (format === 'pkcs8') + key = gostCrypto.asn1.GostPrivateKeyInfo.decode(keyData).object; + else if (format === 'spki') + key = gostCrypto.asn1.GostSubjectPublicKeyInfo.decode(keyData).object; + else + throw new NotSupportedError('Key format not supported'); + + algorithm = normalize(key.algorithm, 'importKey'); + type = key.type; + if (extractable !== false) + extractable = extractable || key.extractable; + if (keyUsages) { + for (var i = 0; i < keyUsages.length; i++) { + if (key.usages.indexOf(keyUsages[i]) < 0) + throw DataError('Key usage not valid for this key'); + } + } else + keyUsages = key.usages; + var data = key.buffer, keySize = algorithm.keySize, dataLen = data.byteLength; + if (type === 'public' || keySize === dataLen) + return data; + else { + // Remove private key masks + if (dataLen % keySize > 0) + throw new DataError('Invalid key size'); + algorithm.mode = 'MASK'; + algorithm.procreator = 'VN'; + var chain = []; + for (var i = keySize; i < dataLen; i += keySize) { + chain.push((function (mask) { + return function (data) { + return execute(algorithm, 'unwrapKey', [mask, data]).then(function (data) { + var next = chain.pop(); + if (next) + return next(data); + else { + delete algorithm.mode; + return data; + } + }); + }; + })(new Uint8Array(data, i, keySize))); + } + return chain.pop()(new Uint8Array(data, 0, keySize)); + } + } + }).then(function (data) { + return convertKey(algorithm, extractable, keyUsages, data, type); + }); + }; // + + /** + * The exportKey method returns a new Promise object that will key data in + * defined format.

+ * The suppored key format values are: + * + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method exportKey + * @instance + * @param {KeyFormat} format Format specifies a serialization format for a key + * @param {Key} key Key object + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ + SubtleCrypto.prototype.exportKey = function (format, key) // + { + return new Promise(call).then(function () { + if (key && checkNative(key.algorithm)) + return rootCrypto.subtle.exportKey(format, key); + + if (!key.extractable) + throw new InvalidAccessError('Key not extractable'); + + var raw = extractKey(null, null, key); + if (format === 'raw') + return raw; + else if (format === 'pkcs8' && key.algorithm && key.algorithm.id) { + if (key.algorithm.procreator === 'VN') { + // Add masks for ViPNet + var algorithm = key.algorithm, mask; + algorithm.mode = 'MASK'; + return execute(algorithm, 'generateKey').then(function (data) { + mask = data; + return execute(algorithm, 'wrapKey', [mask, key.buffer]); + }).then(function (data) { + delete algorithm.mode; + var d = new Uint8Array(data.byteLength + mask.byteLength); + d.set(new Uint8Array(data, 0, data.byteLength)); + d.set(new Uint8Array(mask, 0, mask.byteLength), data.byteLength); + var buffer = d.buffer; + buffer.enclosed = true; + return gostCrypto.asn1.GostPrivateKeyInfo.encode({ + algorithm: algorithm, + buffer: buffer + }); + }); + } else + return gostCrypto.asn1.GostPrivateKeyInfo.encode(key); + } else if (format === 'spki' && key.algorithm && key.algorithm.id) + return gostCrypto.asn1.GostSubjectPublicKeyInfo.encode(key); + else + throw new NotSupportedError('Key format not supported'); + }); + }; // + + /** + * The wrapKey method returns a new Promise object that will wrapped key(s). + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-wrapKey}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method wrapKey + * @instance + * @param {KeyFormat} format Format specifies a serialization format for a key. Now suppored only 'raw' key format. + * @param {Key} key Key object + * @param {Key} wrappingKey Wrapping key object + * @param {AlgorithmIdentifier} wrapAlgorithm Algorithm identifier + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ + SubtleCrypto.prototype.wrapKey = function (format, key, wrappingKey, wrapAlgorithm) // + { + return new Promise(call).then(function () { + if (checkNative(wrapAlgorithm)) + return rootCrypto.subtle.wrapKey(format, key, wrappingKey, wrapAlgorithm); + + wrapAlgorithm = normalize(wrapAlgorithm, 'wrapKey'); + var keyData = extractKey(null, null, key); + if (wrapAlgorithm.procreator === 'SC' && key.type === 'private') + keyData = swapBytes(keyData); + return execute(wrapAlgorithm, 'wrapKey', + [extractKey('wrapKey', wrapAlgorithm, wrappingKey), keyData]).then(function (data) { + if (format === 'raw') + return data; + else + throw new NotSupportedError('Key format not supported'); + }); + }); + }; // + + /** + * The unwrapKey method returns a new Promise object that will unwrapped key(s). + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-unwrapKey}

+ * + * Supported algorithm names: + * + * For additional modes see {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method unwrapKey + * @instance + * @param {KeyFormat} format Format specifies a serialization format for a key. Now suppored only 'raw' key format. + * @param {CryptoOperationData} wrappedKey Wrapped key data + * @param {Key} unwrappingKey Unwrapping key object + * @param {AlgorithmIdentifier} unwrapAlgorithm Algorithm identifier + * @param {AlgorithmIdentifier} unwrappedKeyAlgorithm Key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} + */ + SubtleCrypto.prototype.unwrapKey = function (format, wrappedKey, unwrappingKey, + unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages) // + { + return new Promise(call).then(function () { + if (checkNative(unwrapAlgorithm)) + return rootCrypto.subtle.unwrapKey(format, wrappedKey, unwrappingKey, + unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages); + + unwrapAlgorithm = normalize(unwrapAlgorithm, 'unwrapKey'); + unwrappedKeyAlgorithm = normalize(unwrappedKeyAlgorithm, 'importKey'); + if (format !== 'raw') + throw new NotSupportedError('Key format not supported'); + + return execute(unwrapAlgorithm, 'unwrapKey', [extractKey('unwrapKey', unwrapAlgorithm, unwrappingKey), wrappedKey]).then(function (data) { + var type; + if (unwrappedKeyAlgorithm && unwrappedKeyAlgorithm.name) { + var name = unwrappedKeyAlgorithm.name.toUpperCase().replace(/[\.\s]/g, ''); + if (name.indexOf('3410') >= 0 && keyUsages.indexOf('sign') >= 0) + type = 'private'; + else if (name.indexOf('3410') >= 0 && keyUsages.indexOf('verify') >= 0) + type = 'public'; + } + if (unwrapAlgorithm.procreator === 'SC' && type === 'private') + data = swapBytes(data); + return convertKey(unwrappedKeyAlgorithm, extractable, keyUsages, data, type); + }); + }); + }; // + + /** + * The subtle attribute provides an instance of the SubtleCrypto + * interface which provides low-level cryptographic primitives and + * algorithms. + * + * @memberOf gostCrypto + * @type SubtleCrypto + */ + gostCrypto.subtle = new SubtleCrypto(); + + /** + * The getRandomValues method generates cryptographically random values. + * + * First try to use Web Crypto random genereator. Next make random + * bytes based on standart Math.random mixed with time and mouse pointer + * + * @memberOf gostCrypto + * @param {(CryptoOperationData)} array Destination buffer for random data + */ + gostCrypto.getRandomValues = function (array) // + { + // Execute randomizer + GostRandom = GostRandom || root.GostRandom; + var randomSource = GostRandom ? new GostRandom() : rootCrypto; + if (randomSource.getRandomValues) + randomSource.getRandomValues(array); + else + throw new NotSupportedError('Random generator not found'); + }; // + //
+ + return gostCrypto; +})); diff --git a/src/core/vendor/streebog/gostDigest.js b/src/core/vendor/streebog/gostDigest.js new file mode 100644 index 00000000..aa3ddb41 --- /dev/null +++ b/src/core/vendor/streebog/gostDigest.js @@ -0,0 +1,1269 @@ +/** + * @file GOST R 34.11-94 / GOST R 34.11-12 implementation + * @version 1.76 + * @copyright 2014-2016, Rudolf Nickolaev. All rights reserved. + */ + +/* + * 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. + * + */ + +(function (root, factory) { + + /* + * Module imports and exports + * + */ // + if (typeof exports === 'object') { + module.exports = factory(require('./gostRandom'), require('./gostCipher')); + } + // + +}(this, function (GostRandom, GostCipher) { + + /* + * GOST R 34.11 + * Common methods + * + */ // + + var root = this; + var rootCrypto = root.crypto || root.msCrypto; + + var DataError = root.DataError || Error, + NotSupportedError = root.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 && 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'); + } // + + /** + * Algorithm name GOST R 34.11 or GOST R 34.11-12

+ * + * http://tools.ietf.org/html/rfc6986 + * + * The digest method returns digest data in according to GOST R 4311-2012.
+ * Size of digest also defines in algorithm name. + * + * + * @memberOf GostDigest + * @method digest + * @instance + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {ArrayBuffer} Digest of data + */ + var digest2012 = (function () // + { + // 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; + }; + } // + )(); + + /** + * Algorithm name GOST R 34.11-94

+ * + * 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 () // + { + 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; + }; + + } // + )(); + + /** + * Algorithm name SHA-1

+ * + * https://tools.ietf.org/html/rfc3174 + * + * The digest method returns digest data in according to SHA-1.
+ * + * @memberOf GostDigest + * @method digest + * @instance + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {ArrayBuffer} Digest of data + */ + var digestSHA1 = (function () // + { + + // 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; + }; + + } // + )(); + + /** + * Algorithm name GOST R 34.11-HMAC

+ * + * 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) // + { + // 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); + } // + + /** + * Algorithm name GOST R 34.11-HMAC

+ * + * 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) // + { + 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; + } // + + + /** + * Algorithm name GOST R 34.11-KDF

+ * + * Simple generate key 256/512 bit random seed for derivation algorithms + * + * @memberOf GostDigest + * @method generateKey + * @instance + * @returns {ArrayBuffer} Generated key + */ + function generateKey() // + { + return getSeed(this.bitLength).buffer; + } // + + /** + * Algorithm name GOST R 34.11-PFXKDF

+ * + * Derive bits from password (PKCS12 mode) + * + * @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) // + { + 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; + } // + + /** + * Algorithm name GOST R 34.11-KDF

+ * + * 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) // + { + 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; + } // + + /** + * Algorithm name GOST R 34.11-PBKDF1

+ * + * Derive bits from password + * + * @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) // + { + 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; + } // + + /** + * Algorithm name GOST R 34.11-PBKDF2

+ * + * Derive bits from password + * + * @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) // + { + 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; + } // + + /** + * Algorithm name GOST R 34.11-CPKDF

+ * + * Derive bits from password. CryptoPro algorithm + * + * @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

+ * + * Derive key from derive bits subset + * + * @memberOf GostDigest + * @method deriveKey + * @instance + * @param {ArrayBuffer} baseKey + * @returns {ArrayBuffer} + */ + function deriveKey(baseKey) // + { + return this.deriveBits(baseKey, this.keySize * 8); + } // + + /** + * GOST R 34.11 Algorithm

+ * + * References: {@link http://tools.ietf.org/html/rfc6986} and {@link http://tools.ietf.org/html/rfc5831}

+ * + * Normalized algorithm identifier common parameters: + * + * + * + * Supported algorithms, modes and parameters: + * + * + * + * @class GostDigest + * @param {AlgorithmIdentifier} algorithm WebCryptoAPI algorithm identifier + */ + function GostDigest(algorithm) // + { + + 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'); + } + } // + + return GostDigest; + +})); \ No newline at end of file diff --git a/src/core/vendor/streebog/gostRandom.js b/src/core/vendor/streebog/gostRandom.js new file mode 100644 index 00000000..5282cf0d --- /dev/null +++ b/src/core/vendor/streebog/gostRandom.js @@ -0,0 +1,139 @@ +/** + * @file Implementation Web Crypto random generatore for GOST algorithms + * @version 1.76 + * @copyright 2014-2016, Rudolf Nickolaev. All rights reserved. + */ + +/* + * 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. + * + * 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. + * + */ + +(function (root, factory) { + + /* + * Module imports and exports + * + */ // + if (typeof exports === 'object') { + module.exports = factory(); + } + // + +}(this, function () { + + /** + * The gostCrypto provide general purpose cryptographic functionality for + * GOST standards including a cryptographically strong pseudo-random number + * generator seeded with truly random values. + * + * @Class GostRandom + * + */ // + + var root = this; + var rootCrypto = root.crypto || root.msCrypto; + + var TypeMismatchError = root.TypeMismatchError || Error; + var QuotaExceededError = root.QuotaExceededError || Error; + + // Initialize mouse and time counters for random generator + var randomRing = { + seed: new Uint8Array(1024), + getIndex: 0, + setIndex: 0, + set: function (x) { + if (this.setIndex >= 1024) + this.setIndex = 0; + this.seed[this.setIndex++] = x; + }, + get: function () { + if (this.getIndex >= 1024) + this.getIndex = 0; + return this.seed[this.getIndex++]; + } + }; + + if (typeof document !== 'undefiend') { + try { + // Mouse move event to fill random array + document.addEventListener('mousemove', function (e) { + randomRing.set((new Date().getTime() & 255) ^ + ((e.clientX || e.pageX) & 255) ^ + ((e.clientY || e.pageY) & 255)); + }, false); + } catch (e) { + } + + try { + // Keypress event to fill random array + document.addEventListener('keydown', function (e) { + randomRing.set((new Date().getTime() & 255) ^ + (e.keyCode & 255)); + }, false); + } catch (e) { + } + } // + + function GostRandom() { + } + + /** + * The getRandomValues method generates cryptographically random values.

+ * + * Random generator based on JavaScript Web Crypto random genereator + * (if it is possible) or Math.random mixed with time and parameters of + * mouse and keyboard events + * + * @memberOf GostRandom + * @param {(ArrayBuffer|ArrayBufferView)} array Destination buffer for random data + */ + GostRandom.prototype.getRandomValues = function (array) // + { + + if (!array.byteLength) + throw new TypeMismatchError('Array is not of an integer type (Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, or Uint32Array)'); + + if (array.byteLength > 65536) + throw new QuotaExceededError('Byte length of array can\'t be greate then 65536'); + + var u8 = new Uint8Array(array.buffer, array.byteOffset, array.byteLength); + if (rootCrypto && rootCrypto.getRandomValues) { + // Native window cryptographic interface + rootCrypto.getRandomValues(u8); + } else { + // Standard Javascript method + for (var i = 0, n = u8.length; i < n; i++) + u8[i] = Math.floor(256 * Math.random()) & 255; + } + + // Mix bio randomizator + for (var i = 0, n = u8.length; i < n; i++) + u8[i] = u8[i] ^ randomRing.get(); + return array; + }; // + + return GostRandom; + +}));