diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 4e7c04d0..e55d3659 100755
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -106,7 +106,8 @@
"Enigma",
"Bombe",
"Multiple Bombe",
- "Typex"
+ "Typex",
+ "Streebog"
]
},
{
@@ -197,8 +198,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..7f054fb4
--- /dev/null
+++ b/src/core/operations/Streebog.mjs
@@ -0,0 +1,95 @@
+/**
+ * @author mshwed [m@ttshwed.com]
+ * @copyright Crown Copyright 2019
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import OperationError from "../errors/OperationError";
+import GostCoding from "../vendor/streebog/gostCoding";
+import GostDigest from "../vendor/streebog/gostDigest";
+
+/**
+ * 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 = [
+ {
+ "name": "Version",
+ "type": "option",
+ "value": ["2012", "1994"]
+ },
+ // Paramset sBox for GOST 28147-89. Used only if version = 1994
+ {
+ "name": "S-Box",
+ "type": "option",
+ "value": [
+ "D-A",
+ "D-SC",
+ "E-TEST",
+ "E-A",
+ "E-B",
+ "E-C",
+ "E-D",
+ "E-SC",
+ "E-Z",
+ "D-TEST"
+ ]
+ },
+ // 512 bits digest, valid only for algorithm "Streebog"
+ {
+ "name": "Length",
+ "type": "option",
+ "value": ["256", "512"]
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ try {
+ const version = parseInt(args[0], 10);
+ let sBox = args[1];
+ let length = parseInt(args[2], 10);
+
+ // 1994 old-style 256 bits digest based on GOST 28147-89
+ if (version === 1994) {
+ length = 256;
+ }
+
+ if (version === 2012) {
+ sBox = "";
+ }
+
+ const gostDigest = new GostDigest({name: "GOST R 34.11", version, sBox, length });
+ const gostCoding = new GostCoding();
+
+ const decode = gostCoding.Chars.decode(input);
+ const hexEncode = gostCoding.Hex.encode(gostDigest.digest(decode));
+
+ return hexEncode.replace(/[^\-A-Fa-f0-9]/g, "").toLowerCase();
+ } catch (err) {
+ throw new OperationError(`Invalid Input, Details ${err.message}`);
+ }
+ }
+
+}
+
+export default Streebog;
diff --git a/src/core/vendor/streebog/gostCipher.mjs b/src/core/vendor/streebog/gostCipher.mjs
new file mode 100644
index 00000000..d70d6532
--- /dev/null
+++ b/src/core/vendor/streebog/gostCipher.mjs
@@ -0,0 +1,2259 @@
+/**
+ * GOST 28147-89/GOST R 34.12-2015/GOST R 32.13-2015 Encryption Algorithm
+ * 1.76
+ * 2014-2016, Rudolf Nickolaev. All rights reserved.
+ *
+ * Exported for CyberChef by mshwed [m@ttshwed.com]
+ */
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+import GostRandom from './gostRandom';
+
+import crypto from 'crypto'
+
+/*
+* Initial parameters and common algortithms of GOST 28147-89
+*
+* http://tools.ietf.org/html/rfc5830
+*
+*/ //
+
+var root = {};
+var rootCrypto = crypto;
+var CryptoOperationData = ArrayBuffer;
+var SyntaxError = Error,
+ DataError = Error,
+ NotSupportedError = 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:
+ *
+ *
+ * - name Algorithm name 'GOST 28147' or 'GOST R 34.12'
+ * - version Algorithm version, number
+ *
+ * - 1989 Current version of standard
+ * - 2015 New draft version of standard
+ *
+ *
+ * - length Block length
+ *
+ * - 64 64 bits length (default)
+ * - 128 128 bits length (only for version 2015)
+ *
+ *
+ * - mode Algorithm mode, string
+ *
+ * - ES Encryption mode (default)
+ * - MAC "imitovstavka" (MAC) mode
+ * - KW Key wrapping mode
+ *
+ *
+ * - sBox Paramset sBox for GOST 28147-89, string. Used only if version = 1989
+ *
+ *
+ * Supported algorithms, modes and parameters:
+ *
+ *
+ * - Encript/Decrypt mode (ES)
+ *
+ * - block Block mode, string. Default ECB
+ * - keyMeshing Key meshing mode, string. Default NO
+ * - padding Padding mode, string. Default NO for CFB and CTR modes, or ZERO for others
+ * - iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
+ *
+ *
+ * - Sign/Verify mode (MAC)
+ *
+ * - macLength Length of mac in bits (default - 32 bits)
+ * - iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
+ *
+ *
+ * - Wrap/Unwrap key mode (KW)
+ *
+ * - keyWrapping Mode of keywrapping, string. Default NO - standard GOST key wrapping
+ * - ukm {@link CryptoOperationData} User key material. Default - random generated value
+ *
+ *
+ *
+ *
+ * Supported paramters values:
+ *
+ *
+ * - Block modes (parameter 'block')
+ *
+ * - ECB "prostaya zamena" (ECB) mode (default)
+ * - CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
+ * - OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
+ * - CTR "gammirovanie" (counter) mode
+ * - CBC Cipher-Block-Chaining (CBC) mode
+ *
+ *
+ * - Key meshing modes (parameter 'keyMeshing')
+ *
+ * - NO No key wrapping (default)
+ * - CP CryptoPor Key key meshing
+ *
+ *
+ * - Padding modes (parameter 'padding')
+ *
+ * - NO No padding only for CFB, OFB and CTR modes
+ * - PKCS5 PKCS#5 padding mode
+ * - ZERO Zero bits padding mode
+ * - RANDOM Random bits padding mode
+ * - BIT One bit padding mode
+ *
+ *
+ * - Wrapping key modes (parameter 'keyWrapping')
+ *
+ * - NO Ref. rfc4357 6.1 GOST 28147-89 Key wrapping
+ * - CP CryptoPro Key wrapping mode
+ * - SC SignalCom Key wrapping mode
+ *
+ *
+ *
+ *
+ * @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');
+ }
+} //
+
+export default GostCipher;
diff --git a/src/core/vendor/streebog/gostCoding.mjs b/src/core/vendor/streebog/gostCoding.mjs
new file mode 100644
index 00000000..c9659d52
--- /dev/null
+++ b/src/core/vendor/streebog/gostCoding.mjs
@@ -0,0 +1,1160 @@
+/**
+ * Coding algorithms: Base64, Hex, Int16, Chars, BER and PEM
+ * version 1.76
+ * 2014-2016, Rudolf Nickolaev. All rights reserved.
+ *
+ * Exported for CyberChef by mshwed [m@ttshwed.com]
+ */
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * THIS SOfTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES Of MERCHANTABILITY AND fITNESS fOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * fOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT Of SUBSTITUTE GOODS OR
+ * SERVICES; LOSS Of USE, DATA, OR PROfITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY Of LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT Of THE USE
+ * Of THIS SOfTWARE, EVEN If ADVISED Of THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+import gostCrypto from './gostCrypto';
+
+/**
+ * The Coding interface provides string converting methods: Base64, Hex,
+ * Int16, Chars, BER and PEM
+ * @class GostCoding
+ *
+ */ //
+var root = {};
+var DataError = Error;
+var CryptoOperationData = ArrayBuffer;
+var Date = 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:
+ *
+ * - Empty string or null - NULL
+ * - String starts with '0x' and has 0-9 and a-f characters - INTEGER
+ * - String like d.d.d.d (d - set of digits) - OBJECT IDENTIFIER
+ * - String with characters 0 and 1 - BIT STRING
+ * - Strings 'true' or 'false' - BOOLEAN
+ * - String has only 0-9 and a-f characters - OCTET STRING
+ * - String has only characters with code 0-255 - PrintableString
+ * - Other strings - UTF8String
+ * - Number - INTEGER
+ * - Date - GeneralizedTime
+ * - Boolean - SEQUENCE
+ * - CryptoOperationData - OCTET STRING
+ *
+ * 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:
+ *
+ * - BOOLEAN - Boolean object
+ * - INTEGER, ENUMIRATED - Integer object if len <= 6 (48 bits) else Int16 encoded string
+ * - BIT STRING - Integer object if len <= 5 (w/o unsedBit octet - 32 bits) else String like '10111100' or Array with one item in case of incapsulates encoding
+ * - OCTET STRING - Hex encoded string or Array with one item in case of incapsulates encoding
+ * - OBJECT IDENTIFIER - String with object identifier
+ * - SEQUENCE, SET - Array of encoded items
+ * - UTF8String, NumericString, PrintableString, TeletexString, VideotexString,
+ * IA5String, GraphicString, VisibleString, GeneralString, UniversalString,
+ * BMPString - encoded String
+ * - UTCTime, GeneralizedTime - Date
+ *
+ * @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();
+
+export default GostCoding;
\ No newline at end of file
diff --git a/src/core/vendor/streebog/gostCrypto.mjs b/src/core/vendor/streebog/gostCrypto.mjs
new file mode 100644
index 00000000..0e02f6e4
--- /dev/null
+++ b/src/core/vendor/streebog/gostCrypto.mjs
@@ -0,0 +1,1653 @@
+/**
+ * Implementation Web Crypto interfaces for GOST algorithms
+ * 1.76
+ * 2014-2016, Rudolf Nickolaev. All rights reserved.
+ *
+ * Exported for CyberChef by mshwed [m@ttshwed.com]
+ */
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+import GostRandom from './gostRandom';
+import gostEngine from './gostEngine';
+
+import crypto from 'crypto'
+
+/*
+* Algorithm normalization
+*
+*/ //
+
+var root = {};
+root.gostEngine = gostEngine;
+
+var rootCrypto = crypto
+
+var SyntaxError = Error,
+ DataError = Error,
+ NotSupportedError = Error,
+ OperationError = Error,
+ InvalidStateError = Error,
+ InvalidAccessError = 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:
+ *
+ * - pending: initial state, not fulfilled or rejected.
+ * - fulfilled: successful operation
+ * - rejected: failed operation.
+ *
+ * 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 (!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 || require('./gostEngine');
+}
+
+// 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:
+ *
+ * - 'raw' - An unformatted sequence of bytes. Intended for secret keys.
+ * - 'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.
+ * - 'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
+ * - 'jwk' - The key is represented as JSON according to the JSON Web Key format.
+ *
+ *
+ * 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 = 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:
+ *
+ * - GOST 28147-ECB "prostaya zamena" (ECB) mode (default)
+ * - GOST 28147-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
+ * - GOST 28147-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
+ * - GOST 28147-CTR "gammirovanie" (counter) mode
+ * - GOST 28147-CBC Cipher-Block-Chaining (CBC) mode
+ * - GOST R 34.12-ECB "prostaya zamena" (ECB) mode (default)
+ * - GOST R 34.12-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
+ * - GOST R 34.12-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
+ * - GOST R 34.12-CTR "gammirovanie" (counter) mode
+ * - GOST R 34.12-CBC Cipher-Block-Chaining (CBC) mode
+ *
+ * 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:
+ *
+ * - GOST 28147-ECB "prostaya zamena" (ECB) mode (default)
+ * - GOST 28147-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
+ * - GOST 28147-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
+ * - GOST 28147-CTR "gammirovanie" (counter) mode
+ * - GOST 28147-CBC Cipher-Block-Chaining (CBC) mode
+ * - GOST R 34.12-ECB "prostaya zamena" (ECB) mode (default)
+ * - GOST R 34.12-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
+ * - GOST R 34.12-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
+ * - GOST R 34.12-CTR "gammirovanie" (counter) mode
+ * - GOST R 34.12-CBC Cipher-Block-Chaining (CBC) mode
+ *
+ * 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:
+ *
+ * - GOST R 34.10-94 GOST Signature
+ * - GOST R 34.10-94/GOST R 34.11-94 GOST Signature with Hash
+ * - GOST R 34.10 ECGOST Signature
+ * - GOST R 34.10/GOST R 34.11-94 ECGOST Signature with Old-Style Hash
+ * - GOST R 34.10/GOST R 34.11 ECGOST Signature with Streebog Hash
+ * - GOST 28147-MAC MAC base on GOST 28147
+ * - GOST R 34.12-MAC MAC base on GOST R 43.12
+ * - GOST R 34.11-HMAC HMAC base on GOST 34.11
+ * - SHA-HMAC HMAC base on SHA
+ *
+ * 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:
+ *
+ * - GOST R 34.10-94 GOST Signature
+ * - GOST R 34.10-94/GOST R 34.11-94 GOST Signature with Hash
+ * - GOST R 34.10 ECGOST Signature
+ * - GOST R 34.10/GOST R 34.11-94 ECGOST Signature with Old-Style Hash
+ * - GOST R 34.10/GOST R 34.11 ECGOST Signature with Streebog Hash
+ * - GOST 28147-MAC MAC base on GOST 28147
+ * - GOST R 34.12-MAC MAC base on GOST R 34.12
+ * - GOST R 34.11-HMAC HMAC base on GOST 34.11
+ * - SHA-HMAC HMAC base on SHA
+ *
+ * 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:
+ *
+ * - GOST R 34.11-94 Old-Style GOST Hash
+ * - GOST R 34.11 GOST Streebog Hash
+ * - SHA SHA Hash
+ *
+ * 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:
+ *
+ * - GOST R 34.10 ECGOST Key Pairs
+ * - GOST 28147 Key for encryption GOST 28147 modes
+ * - GOST 28147-KW Key for wrapping GOST 28147 modes
+ * - GOST R 34.12 Key for encryption GOST R 34.12 modes
+ * - GOST R 34.12-KW Key for wrapping GOST R 34.12 modes
+ * - GOST R 34.11-KDF Key for Derivation Algorithm
+ *
+ * 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:
+ *
+ * - GOST R 34.10-DH ECDH Key Agreement mode
+ * - GOST R 34.11-KDF Key for Derivation Algorithm
+ * - GOST R 34.11-PBKDF2 Password Based Key for Derivation Algorithm
+ * - GOST R 34.11-PFXKDF PFX Key for Derivation Algorithm
+ * - GOST R 34.11-CPKDF Password Based Key for CryptoPro Derivation Algorithm
+ * - SHA-PBKDF2 Password Based Key for Derivation Algorithm
+ * - SHA-PFXKDF PFX Key for Derivation Algorithm
+ *
+ * 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:
+ *
+ * - GOST R 34.10-DH ECDH Key Agreement mode
+ * - GOST R 34.11-KDF Key for Derivation Algorithm
+ * - GOST R 34.11-PBKDF2 Password Based Key for Derivation Algorithm
+ * - GOST R 34.11-PFXKDF PFX Key for Derivation Algorithm
+ * - GOST R 34.11-CPKDF Password Based Key for CryptoPro Derivation Algorithm
+ * - SHA-PBKDF2 Password Based Key for Derivation Algorithm
+ * - SHA-PFXKDF PFX Key for Derivation Algorithm
+ *
+ * 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:
+ *
+ * - 'raw' - An unformatted sequence of bytes. Intended for secret keys.
+ * - 'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.
+ * - 'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
+ *
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey}
+ *
+ * Supported algorithm names:
+ *
+ * - GOST R 34.10-94 GOST Private and Public keys
+ * - GOST R 34.10 ECGOST Private and Public keys
+ * - GOST 28147 Key for encryption GOST 28147 modes
+ * - GOST 28147-KW Key for key wrapping GOST 28147 modes
+ * - GOST R 34.12 Key for encryption GOST 34.12 modes
+ * - GOST R 34.12-KW Key for key wrapping GOST 34.12 modes
+ * - GOST R 34.11-KDF Key for Derivation Algorithm
+ *
+ * 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:
+ *
+ * - 'raw' - An unformatted sequence of bytes. Intended for secret keys.
+ * - 'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.
+ * - 'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
+ *
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey}
+ *
+ * Supported algorithm names:
+ *
+ * - GOST R 34.10-94 GOST Private and Public keys
+ * - GOST R 34.10 ECGOST Private and Public keys
+ * - GOST 28147 Key for encryption GOST 28147 modes
+ * - GOST 28147-KW Key for key wrapping GOST 28147 modes
+ * - GOST R 34.12 Key for encryption GOST R 34.12 modes
+ * - GOST R 34.12-KW Key for key wrapping GOST R 34.12 modes
+ * - GOST R 34.11-KDF Key for Derivation Algorithm
+ * - GOST R 34.11-PBKDF2 Import Password for Key for Derivation Algorithm
+ * - GOST R 34.11-PFXKDF Import PFX Key for Derivation Algorithm
+ * - GOST R 34.11-CPKDF Import Password Key for CryptoPro Derivation Algorithm
+ * - SHA-PBKDF2 Import Password for Key for Derivation Algorithm
+ * - SHA-PFXKDF Import PFX Key for Derivation Algorithm
+ *
+ * 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:
+ *
+ * - GOST 28147-KW Key Wrapping GOST 28147 modes
+ * - GOST R 34.12-KW Key Wrapping GOST R 34.12 modes
+ * - GOST 28147-MASK Key Mask GOST 28147 modes
+ * - GOST R 34.12-MASK Key Mask GOST R 34.12 modes
+ * - GOST R 34.10-MASK Key Mask GOST R 34.10 modes
+ *
+ * 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:
+ *
+ * - GOST 28147-KW Key Wrapping GOST 28147 modes
+ * - GOST R 34.12-KW Key Wrapping GOST R 34.12 modes
+ * - GOST 28147-MASK Key Mask GOST 28147 modes
+ * - GOST R 34.12-MASK Key Mask GOST R 34.12 modes
+ * - GOST R 34.10-MASK Key Mask GOST R 34.10 modes
+ *
+ * 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');
+}; //
+//
+
+export default gostCrypto;
diff --git a/src/core/vendor/streebog/gostDigest.mjs b/src/core/vendor/streebog/gostDigest.mjs
new file mode 100644
index 00000000..0ead2b79
--- /dev/null
+++ b/src/core/vendor/streebog/gostDigest.mjs
@@ -0,0 +1,1260 @@
+/**
+ * GOST R 34.11-94 / GOST R 34.11-12 implementation
+ * 1.76
+ * 2014-2016, Rudolf Nickolaev. All rights reserved.
+ *
+ * Exported for CyberChef by mshwed [m@ttshwed.com]
+ */
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Converted to JavaScript from source https://www.streebog.net/
+ * Copyright (c) 2013, Alexey Degtyarev.
+ * All rights reserved.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+ import GostRandom from './gostRandom';
+ import GostCipher from './gostCipher';
+ import crypto from 'crypto';
+
+/*
+ * GOST R 34.11
+ * Common methods
+ *
+ */ //
+
+var root = {};
+var rootCrypto = crypto
+
+var DataError = Error,
+ NotSupportedError = Error;
+
+// Copy len values from s[sOfs] to d[dOfs]
+function arraycopy(s, sOfs, d, dOfs, len) {
+ for (var i = 0; i < len; i++)
+ d[dOfs + i] = s[sOfs + i];
+}
+
+// Swap bytes in buffer
+function swap(s) {
+ var src = new Uint8Array(s),
+ dst = new Uint8Array(src.length);
+ for (var i = 0, n = src.length; i < n; i++)
+ dst[n - i - 1] = src[i];
+ return dst.buffer;
+}
+
+// Convert BASE64 string to Uint8Array
+// for decompression of constants and precalc values
+function b64decode(s) {
+ // s = s.replace(/[^A-Za-z0-9\+\/]/g, '');
+ var n = s.length,
+ k = n * 3 + 1 >> 2, r = new Uint8Array(k);
+
+ for (var m3, m4, u24 = 0, j = 0, i = 0; i < n; i++) {
+ m4 = i & 3;
+ var c = s.charCodeAt(i);
+
+ c = c > 64 && c < 91 ?
+ c - 65 : c > 96 && c < 123 ?
+ c - 71 : c > 47 && c < 58 ?
+ c + 4 : c === 43 ?
+ 62 : c === 47 ?
+ 63 : 0;
+
+ u24 |= c << 18 - 6 * m4;
+ if (m4 === 3 || n - i === 1) {
+ for (m3 = 0; m3 < 3 && j < k; m3++, j++) {
+ r[j] = u24 >>> (16 >>> m3 & 24) & 255;
+ }
+ u24 = 0;
+
+ }
+ }
+ return r.buffer;
+}
+
+// Random seed
+function getSeed(length) {
+ GostRandom = GostRandom || root.GostRandom;
+ var randomSource = GostRandom ? new (GostRandom || root.GostRandom) : rootCrypto;
+ if (randomSource.getRandomValues) {
+ var d = new Uint8Array(Math.ceil(length / 8));
+ randomSource.getRandomValues(d);
+ return d;
+ } else
+ throw new NotSupportedError('Random generator not found');
+}
+
+// Check buffer
+function buffer(d) {
+ if (d instanceof ArrayBuffer)
+ return d;
+ else if (d && d.buffer && 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.
+ *
+ * - GOST R 34.11-256-12 - 256 bits digest
+ * - GOST R 34.11-512-12 - 512 bits digest
+ *
+ *
+ * @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)
+ *
+ * - algorithm.salt - random value, salt
+ * - algorithm.iterations - number of iterations
+ *
+ * @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
+ *
+ * - algorithm.salt - random value, salt
+ * - algorithm.iterations - number of iterations
+ *
+ * @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
+ *
+ * - algorithm.salt - random value, salt
+ * - algorithm.iterations - number of iterations
+ *
+ * @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
+ *
+ * - algorithm.salt - random value, salt
+ * - algorithm.iterations - number of iterations
+ *
+ * @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:
+ *
+ *
+ * - name Algorithm name 'GOST R 34.11'
+ * - version Algorithm version
+ *
+ * - 1994 old-style 256 bits digest based on GOST 28147-89
+ * - 2012 256 ro 512 bits digest algorithm "Streebog" GOST R 34.11-2012 (default)
+ *
+ *
+ * - length Digest length
+ *
+ * - 256 256 bits digest
+ * - 512 512 bits digest, valid only for algorithm "Streebog"
+ *
+ *
+ * - mode Algorithm mode
+ *
+ * - HASH simple digest mode (default)
+ * - HMAC HMAC algorithm based on GOST R 34.11
+ * - KDF Derive bits for KEK deversification
+ * - PBKDF2 Password based key dirivation algorithms PBKDF2 (based on HMAC)
+ * - PFXKDF Password based PFX key dirivation algorithms
+ * - CPKDF CpyptoPro Password based key dirivation algorithms
+ *
+ *
+ * - sBox Paramset sBox for GOST 28147-89. Used only if version = 1994
+ *
+ *
+ * Supported algorithms, modes and parameters:
+ *
+ *
+ * - Digest HASH mode (default)
+ * - Sign/Verify HMAC modes parameters depends on version and length
+ *
+ * - version: 1994 HMAC parameters (B = 32, L = 32)
+ * - version: 2012, length: 256 HMAC parameters (B = 64, L = 32)
+ * - version: 2012, length: 512 HMAC parameters (B = 64, L = 64)
+ *
+ *
+ * - DeriveBits/DeriveKey KDF mode
+ *
+ * - context {@link CryptoOperationData} Context of the key derivation
+ * - label {@link CryptoOperationData} Label that identifies the purpose for the derived keying material
+ *
+ *
+ * - DeriveBits/DeriveKey PBKDF2 mode
+ *
+ * - salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
+ * - iterations Iteration count. GOST recomended value 1000 (default) or 2000
+ * - diversifier Deversifier, ID=1 - key material for performing encryption or decryption, ID=3 - integrity key for MACing
+ *
+ *
+ * - DeriveBits/DeriveKey PFXKDF mode
+ *
+ * - salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
+ * - iterations Iteration count. GOST recomended value 1000 (default) or 2000
+ * - diversifier Deversifier, ID=1 - key material for performing encryption or decryption,
+ * ID=2 - IV (Initial Value) for encryption or decryption, ID=3 - integrity key for MACing
+ *
+ *
+ * - DeriveBits/DeriveKey CPKDF mode
+ *
+ * - salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
+ * - iterations Iteration count. GOST recomended value 1000 (default) or 2000
+ *
+ *
+ *
+ *
+ * @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');
+ }
+} //
+
+export default GostDigest;
\ No newline at end of file
diff --git a/src/core/vendor/streebog/gostEngine.mjs b/src/core/vendor/streebog/gostEngine.mjs
new file mode 100755
index 00000000..be8fd4e8
--- /dev/null
+++ b/src/core/vendor/streebog/gostEngine.mjs
@@ -0,0 +1,451 @@
+/**
+ * @file GOST 34.10-2012 signature function with 1024/512 bits digest
+ * @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.
+ *
+ */
+
+import GostRandom from './gostRandom';
+import GostCipher from './gostCipher';
+import GostDigest from './gostDigest';
+import GostSign from './gostSign';
+
+/*
+ * Engine definition base on normalized algorithm identifier
+ *
+ */ //
+
+var root = {};
+
+// Define engine
+function defineEngine(method, algorithm) {
+ if (!algorithm)
+ throw new (root.SyntaxError || Error)('Algorithm not defined');
+
+ if (!algorithm.name)
+ throw new (root.SyntaxError || Error)('Algorithm name not defined');
+
+ var name = algorithm.name, mode = algorithm.mode;
+ if ((name === 'GOST 28147' || name === 'GOST R 34.12' || name === 'RC2') && (method === 'generateKey' ||
+ (mode === 'MAC' && (method === 'sign' || method === 'verify')) ||
+ ((mode === 'KW' || mode === 'MASK') && (method === 'wrapKey' || method === 'unwrapKey')) ||
+ ((!mode || mode === 'ES') && (method === 'encrypt' || method === 'decrypt')))) {
+ return 'GostCipher';
+
+ } else if ((name === 'GOST R 34.11' || name === 'SHA') && (method === 'digest' ||
+ (mode === 'HMAC' && (method === 'sign' || method === 'verify' || method === 'generateKey')) ||
+ ((mode === 'KDF' || mode === 'PBKDF2' || mode === 'PFXKDF' || mode === 'CPKDF') &&
+ (method === 'deriveKey' || method === 'deriveBits' || method === 'generateKey')))) {
+ return 'GostDigest';
+
+ } else if (name === 'GOST R 34.10' && (method === 'generateKey' ||
+ ((!mode || mode === 'SIGN') && (method === 'sign' || method === 'verify')) ||
+ (mode === 'MASK' && (method === 'wrapKey' || method === 'unwrapKey')) ||
+ (mode === 'DH' && (method === 'deriveKey' || method === 'deriveBits')))) {
+ return 'GostSign';
+ } else
+ throw new (root.NotSupportedError || Error)('Algorithm ' + name + '-' + mode + ' is not valid for ' + method);
+} //
+
+/**
+ * Object implements dedicated Web Workers and provide a simple way to create
+ * and run GOST cryptographic algorithms in background thread.
+ *
+ * Object provide interface to GOST low-level cryptogric classes:
+ *
+ * - GostCipher - implementation of GOST 28147, GOST R 34.12, GOST R 34.13 Encryption algorithms. Reference {@link http://tools.ietf.org/html/rfc5830}
+ * - GostDigest - implementation of GOST R 34.11 Hash Function algorithms. References {@link http://tools.ietf.org/html/rfc5831} and {@link http://tools.ietf.org/html/rfc6986}
+ * - GostSign - implementation of GOST R 34.10 Digital Signature algorithms. References {@link http://tools.ietf.org/html/rfc5832} and {@link http://tools.ietf.org/html/rfc7091}
+ *
+ * @namespace gostEngine
+ */
+var gostEngine = {
+ /**
+ * gostEngine.execute(algorithm, method, args) Entry point to execution
+ * all low-level GOST cryptographic methods
+ *
+ *
+ * - Determine the appropriate engine for a given execution method
+ * - Create cipher object for determineted engine
+ * - Execute method of cipher with given args
+ *
+ *
+ * @memberOf gostEngine
+ * @param {AlgorithmIndentifier} algorithm Algorithm identifier
+ * @param {string} method Crypto method for execution
+ * @param {Array} args Method arguments (keys, data, additional parameters)
+ * @returns {(CryptoOperationData|Key|KeyPair|boolean)} Result of method execution
+ */
+ execute: function (algorithm, method, args) //
+ {
+ // Define engine for GOST algorithms
+ var engine = defineEngine(method, algorithm);
+ // Create cipher
+ var cipher = this['get' + engine](algorithm);
+ // Execute method
+ return cipher[method].apply(cipher, args);
+ }, //
+ /**
+ * gostEngine.getGostCipher(algorithm) returns GOST 28147 / GOST R 34.12 cipher instance
+ *
+ * GOST 28147-89 / GOST R 34.12-15 Encryption Algorithm
+ * When keys and initialization vectors are converted to/from byte arrays,
+ * little-endian byte order is assumed.
+ *
+ * Normalized algorithm identifier common parameters:
+ *
+ *
+ * - name Algorithm name 'GOST 28147' or 'GOST R 34.12'
+ * - version Algorithm version, number
+ *
+ * - 1989 Current version of standard
+ * - 2015 New draft version of standard
+ *
+ *
+ * - length Block length
+ *
+ * - 64 64 bits length (default)
+ * - 128 128 bits length (only for version 2015)
+ *
+ *
+ * - mode Algorithm mode, string
+ *
+ * - ES Encryption mode (default)
+ * - MAC "imitovstavka" (MAC) mode
+ * - KW Key wrapping mode
+ * - MASK Key mask mode
+ *
+ *
+ * - sBox Paramset sBox for GOST 28147-89, string. Used only if version = 1989
+ *
+ *
+ * Supported algorithms, modes and parameters:
+ *
+ *
+ * - Encript/Decrypt mode (ES)
+ *
+ * - block Block mode, string. Default ECB
+ * - keyMeshing Key meshing mode, string. Default NO
+ * - padding Padding mode, string. Default NO for CFB and CTR modes, or ZERO for others
+ * - iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
+ *
+ *
+ * - Sign/Verify mode (MAC)
+ *
+ * - macLength Length of mac in bits (default - 32 bits)
+ * - iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
+ *
+ *
+ * - Wrap/Unwrap key mode (KW)
+ *
+ * - keyWrapping Mode of keywrapping, string. Default NO - standard GOST key wrapping
+ * - ukm {@link CryptoOperationData} User key material. Default - random generated value
+ *
+ *
+ * - Wrap/Unwrap key mode (MASK)
+ *
+ *
+ * Supported paramters values:
+ *
+ *
+ * - Block modes (parameter 'block')
+ *
+ * - ECB "prostaya zamena" (ECB) mode (default)
+ * - CFB "gammirovanie s obratnoj svyaziyu" (64-bit CFB) mode
+ * - CTR "gammirovanie" (counter) mode
+ * - CBC Cipher-Block-Chaining (CBC) mode
+ *
+ *
+ * - Key meshing modes (parameter 'keyMeshing')
+ *
+ * - NO No key wrapping (default)
+ * - CP CryptoPor Key key meshing
+ *
+ *
+ * - Padding modes (parameter 'padding')
+ *
+ * - NO No padding only for CFB and CTR modes
+ * - PKCS5 PKCS#5 padding mode
+ * - ZERO Zero bits padding mode
+ * - RANDOM Random bits padding mode
+ *
+ *
+ * - Wrapping key modes (parameter 'keyWrapping')
+ *
+ * - NO Ref. rfc4357 6.1 GOST 28147-89 Key wrapping
+ * - CP CryptoPro Key wrapping mode
+ * - SC SignalCom Key wrapping mode
+ *
+ *
+ *
+ *
+ * @memberOf gostEngine
+ * @param {AlgorithmIndentifier} algorithm Algorithm identifier
+ * @returns {GostCipher} Instance of GostCipher
+ */
+ getGostCipher: function (algorithm) //
+ {
+ return new (GostCipher || (GostCipher = root.GostCipher))(algorithm);
+ }, //
+ /**
+ * gostEngine.getGostDigest(algorithm) returns GOST R 34.11 cipher instance
+ *
+ * Normalized algorithm identifier common parameters:
+ *
+ *
+ * - name Algorithm name 'GOST R 34.11'
+ * - version Algorithm version
+ *
+ * - 1994 old-style 256 bits digest based on GOST 28147-89
+ * - 2012 256 ro 512 bits digest algorithm "Streebog" GOST R 34.11-2012 (default)
+ *
+ *
+ * - length Digest length
+ *
+ * - 256 256 bits digest
+ * - 512 512 bits digest, valid only for algorithm "Streebog"
+ *
+ *
+ * - mode Algorithm mode
+ *
+ * - HASH simple digest mode (default)
+ * - HMAC HMAC algorithm based on GOST R 34.11
+ * - KDF Derive bits for KEK deversification
+ * - PBKDF2 Password based key dirivation algorithms PBKDF2 (based on HMAC)
+ * - PFXKDF PFX key dirivation algorithms PFXKDF
+ * - CPKDF CryptoPro Password based key dirivation algorithms
+ *
+ *
+ * - sBox Paramset sBox for GOST 28147-89. Used only if version = 1994
+ *
+ *
+ * Supported algorithms, modes and parameters:
+ *
+ *
+ * - Digest HASH mode (default)
+ * - Sign/Verify HMAC modes parameters depends on version and length
+ *
+ * - version: 1994 HMAC parameters (B = 32, L = 32)
+ * - version: 2012, length: 256 HMAC parameters (B = 64, L = 32)
+ * - version: 2012, length: 512 HMAC parameters (B = 64, L = 64)
+ *
+ *
+ * - DeriveBits/DeriveKey KDF mode
+ *
+ * - context {@link CryptoOperationData} Context of the key derivation
+ * - label {@link CryptoOperationData} Label that identifies the purpose for the derived keying material
+ *
+ *
+ * - DeriveBits/DeriveKey PBKDF2 mode
+ *
+ * - salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
+ * - iterations Iteration count. GOST recomended value 1000 (default) or 2000
+ *
+ *
+ * - DeriveBits/DeriveKey PFXKDF mode
+ *
+ * - salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
+ * - iterations Iteration count. GOST recomended value 1000 (default) or 2000
+ * - diversifier Deversifier, ID=1 - key material for performing encryption or decryption,
+ * ID=2 - IV (Initial Value) for encryption or decryption, ID=3 - integrity key for MACing
+ *
+ *
+ * - DeriveBits/DeriveKey CPKDF mode
+ *
+ * - salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
+ * - iterations Iteration count. GOST recomended value 1000 (default) or 2000
+ *
+ *
+ *
+ *
+ * @memberOf gostEngine
+ * @param {AlgorithmIndentifier} algorithm Algorithm identifier
+ * @returns {GostDigest} Instance of GostDigest
+ */
+ getGostDigest: function (algorithm) //
+ {
+ return new (GostDigest || (GostDigest = root.GostDigest))(algorithm);
+ }, //
+ /**
+ * gostEngine.getGostSign(algorithm) returns GOST R 34.10 cipher instance
+ *
+ * Normalized algorithm identifier common parameters:
+ *
+ *
+ * - name Algorithm name 'GOST R 34.10'
+ * - version Algorithm version
+ *
+ * - 1994 - Old-style GOST R 34.10-94 ExpMod algorithm with GOST R 34.11-94 hash
+ * - 2001 - GOST R 34.10-2001 Eliptic curve algorithm with old GOST R 34.11-94 hash
+ * - 2012 - GOST R 34.10-2012 Eliptic curve algorithm with GOST R 34.11-12 hash, default mode
+ *
+ *
+ * - length Length of hash and signature. Key length == hash length for EC algorithms and 2 * hash length for ExpMod algorithm
+ *
+ * - GOST R 34.10-256 - 256 bits digest, default mode
+ * - GOST R 34.10-512 - 512 bits digest only for GOST R 34.11-2012 hash
+ *
+ *
+ * - mode Algorithm mode
+ *
+ * - SIGN Digital signature mode (default)
+ * - DH Diffie-Hellman key generation and key agreement mode
+ * - MASK Key mask mode
+ *
+ *
+ * - sBox Paramset sBox for GOST 34.11-94. Used only if version = 1994 or 2001
+ *
+ *
+ * Supported algorithms, modes and parameters:
+ *
+ *
+ * - Sign/Verify mode (SIGN)
+ * - Wrap/Unwrap mode (MASK)
+ * - DeriveKey/DeriveBits mode (DH)
+ *
+ * - {@link CryptoOperationData} ukm User key material. Default - random generated value
+ * - {@link CryptoOperationData} public The peer's EC public key data
+ *
+ *
+ * - GenerateKey mode (SIGN and DH and MASK) version = 1994
+ *
+ * - namedParam Paramset for key generation algorithm. If specified no additianal parameters required
+ *
+ * Additional parameters, if namedParam not specified
+ *
+ * - modulusLength Bit length of p (512 or 1024 bits). Default = 1024
+ * - p {@link CryptoOperationData} Modulus, prime number, 2^(t-1)
+ *
- q {@link CryptoOperationData} Order of cyclic group, prime number, 2^254
+ * - a {@link CryptoOperationData} Generator, integer, 1
+ *
+ *
+ * - GenerateKey mode (SIGN and DH and MASK) version = 2001 or 2012
+ *
+ * - namedCurve Paramset for key generation algorithm. If specified no additianal parameters required
+ *
+ * Additional EC parameters, if namedCurve not specified
+ *
+ * - p {@link CryptoOperationData} Prime number - elliptic curve modulus
+ * - a {@link CryptoOperationData} Coefficients a of the elliptic curve E
+ * - b {@link CryptoOperationData} Coefficients b of the elliptic curve E
+ * - q {@link CryptoOperationData} Prime number - order of cyclic group
+ * - x {@link CryptoOperationData} Base point p x-coordinate
+ * - y {@link CryptoOperationData} Base point p y-coordinate
+ *
+ *
+ *
+ *
+ * @memberOf gostEngine
+ * @param {AlgorithmIndentifier} algorithm Algorithm identifier
+ * @returns {GostSign} Instance of GostSign
+ */
+ getGostSign: function (algorithm) //
+ {
+ return new (GostSign || (GostSign = root.GostSign))(algorithm);
+ } //
+};
+
+/*
+ * Worker method execution
+ *
+ */ //
+
+// Worker for gostCripto method execution
+if (root.importScripts) {
+
+ /**
+ * Method called when {@link SubtleCrypto} calls its own postMessage()
+ * method with data parameter: algorithm, method and arg.
+ * Call method execute and postMessage() results to onmessage event handler
+ * in the main process.
+ * If error occured onerror event handler executed in main process.
+ *
+ * @memberOf gostEngine
+ * @name onmessage
+ * @param {MessageEvent} event Message event with data {algorithm, method, args}
+ */
+ root.onmessage = function (event) {
+ try {
+ postMessage({
+ id: event.data.id,
+ result: gostEngine.execute(event.data.algorithm,
+ event.data.method, event.data.args)});
+ } catch (e) {
+ postMessage({
+ id: event.data.id,
+ error: e.message
+ });
+ }
+ };
+} else {
+
+ // Load dependens
+ 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
+ var importScripts = function () {
+ 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);
+ }
+ }
+ };
+
+ // Import engines
+ if (!GostRandom)
+ importScripts('gostRandom.js');
+ if (!GostCipher)
+ importScripts('gostCipher.js');
+ if (!GostDigest)
+ importScripts('gostDigest.js');
+ if (!GostSign)
+ importScripts('gostSign.js');
+} //
+
+export default gostEngine;
+
diff --git a/src/core/vendor/streebog/gostRandom.mjs b/src/core/vendor/streebog/gostRandom.mjs
new file mode 100644
index 00000000..03ce8937
--- /dev/null
+++ b/src/core/vendor/streebog/gostRandom.mjs
@@ -0,0 +1,128 @@
+/**
+ * Implementation Web Crypto random generatore for GOST algorithms
+ * 1.76
+ * 2014-2016, Rudolf Nickolaev. All rights reserved.
+ *
+ * Exported for CyberChef by mshwed [m@ttshwed.com]
+ */
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+import crypto from 'crypto';
+
+
+/**
+ * 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 = {};
+var rootCrypto = crypto;
+
+var TypeMismatchError = Error;
+var 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 !== 'undefined') {
+ 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;
+}; //
+
+export default GostRandom;
diff --git a/src/core/vendor/streebog/gostSign.mjs b/src/core/vendor/streebog/gostSign.mjs
new file mode 100755
index 00000000..31c6b572
--- /dev/null
+++ b/src/core/vendor/streebog/gostSign.mjs
@@ -0,0 +1,2023 @@
+/**
+ * @file GOST 34.10-2012 signature function with 1024/512 bits digest
+ * @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.
+ *
+ * Used library JSBN http://www-cs-students.stanford.edu/~tjw/jsbn/
+ * Copyright (c) 2003-2005 Tom Wu (tjw@cs.Stanford.EDU)
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+ import GostRandom from './gostRandom';
+ import GostDigest from './gostDigest';
+
+ import crypto from 'crypto';
+
+ /*
+ * Predefined curves and params collection
+ *
+ * http://tools.ietf.org/html/rfc5832
+ * http://tools.ietf.org/html/rfc7091
+ * http://tools.ietf.org/html/rfc4357
+ *
+ */ //
+
+var root = {};
+var rootCrypto = crypto;
+var CryptoOperationData = ArrayBuffer;
+
+var OperationError = Error,
+ DataError = Error,
+ NotSupportedError = Error;
+
+// Predefined named curve collection
+var ECGostParams = {
+ 'S-256-TEST': {
+ a: 7,
+ b: '0x5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E',
+ p: '0x8000000000000000000000000000000000000000000000000000000000000431',
+ q: '0x8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3',
+ x: 2,
+ y: '0x8E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8'
+ },
+ 'S-256-A': {
+ a: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94',
+ b: 166,
+ p: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97',
+ q: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893',
+ x: 1,
+ y: '0x8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14'
+ },
+ 'S-256-B': {
+ a: '0x8000000000000000000000000000000000000000000000000000000000000C96',
+ b: '0x3E1AF419A269A5F866A7D3C25C3DF80AE979259373FF2B182F49D4CE7E1BBC8B',
+ p: '0x8000000000000000000000000000000000000000000000000000000000000C99',
+ q: '0x800000000000000000000000000000015F700CFFF1A624E5E497161BCC8A198F',
+ x: 1,
+ y: '0x3FA8124359F96680B83D1C3EB2C070E5C545C9858D03ECFB744BF8D717717EFC'
+ },
+ 'S-256-C': {
+ a: '0x9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598',
+ b: 32858,
+ p: '0x9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B',
+ q: '0x9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9',
+ x: 0,
+ y: '0x41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67'
+ },
+ 'P-256': {
+ p: '0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF',
+ a: '0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC',
+ b: '0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B',
+ x: '0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296',
+ y: '0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5',
+ q: '0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551'
+ },
+ 'T-512-TEST': {
+ a: 7,
+ b: '0x1CFF0806A31116DA29D8CFA54E57EB748BC5F377E49400FDD788B649ECA1AC4361834013B2AD7322480A89CA58E0CF74BC9E540C2ADD6897FAD0A3084F302ADC',
+ p: '0x4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DF1D852741AF4704A0458047E80E4546D35B8336FAC224DD81664BBF528BE6373',
+ q: '0x4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DA82F2D7ECB1DBAC719905C5EECC423F1D86E25EDBE23C595D644AAF187E6E6DF',
+ x: '0x24D19CC64572EE30F396BF6EBBFD7A6C5213B3B3D7057CC825F91093A68CD762FD60611262CD838DC6B60AA7EEE804E28BC849977FAC33B4B530F1B120248A9A',
+ y: '0x2BB312A43BD2CE6E0D020613C857ACDDCFBF061E91E5F2C3F32447C259F39B2C83AB156D77F1496BF7EB3351E1EE4E43DC1A18B91B24640B6DBB92CB1ADD371E'
+ },
+ 'T-512-A': {
+ p: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7',
+ a: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4',
+ b: '0xE8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760',
+ q: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275',
+ x: 3,
+ y: '0x7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4'
+ },
+ 'T-512-B': {
+ p: '0x8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F',
+ a: '0x8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C',
+ b: '0x687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116',
+ q: '0x800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD',
+ x: 2,
+ y: '0x1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD'
+ }
+};
+ECGostParams['X-256-A'] = ECGostParams['S-256-A'];
+ECGostParams['X-256-B'] = ECGostParams['S-256-C'];
+ECGostParams['T-256-TEST'] = ECGostParams['S-256-TEST'];
+ECGostParams['T-256-A'] = ECGostParams['S-256-A'];
+ECGostParams['T-256-B'] = ECGostParams['S-256-B'];
+ECGostParams['T-256-C'] = ECGostParams['S-256-C'];
+
+
+var GostParams = {
+ 'S-TEST': {
+ modulusLength: 512, // bit length of p (512 or 1024 bits)
+ p: '0xEE8172AE8996608FB69359B89EB82A69854510E2977A4D63BC97322CE5DC3386EA0A12B343E9190F23177539845839786BB0C345D165976EF2195EC9B1C379E3',
+ q: '0x98915E7EC8265EDFCDA31E88F24809DDB064BDC7285DD50D7289F0AC6F49DD2D',
+ a: '0x9e96031500c8774a869582d4afde2127afad2538b4b6270a6f7c8837b50d50f206755984a49e509304d648be2ab5aab18ebe2cd46ac3d8495b142aa6ce23e21c'
+ },
+ 'S-A': {
+ modulusLength: 1024,
+ p: '0xB4E25EFB018E3C8B87505E2A67553C5EDC56C2914B7E4F89D23F03F03377E70A2903489DD60E78418D3D851EDB5317C4871E40B04228C3B7902963C4B7D85D52B9AA88F2AFDBEB28DA8869D6DF846A1D98924E925561BD69300B9DDD05D247B5922D967CBB02671881C57D10E5EF72D3E6DAD4223DC82AA1F7D0294651A480DF',
+ q: '0x972432A437178B30BD96195B773789AB2FFF15594B176DD175B63256EE5AF2CF',
+ a: '0x8FD36731237654BBE41F5F1F8453E71CA414FFC22C25D915309E5D2E62A2A26C7111F3FC79568DAFA028042FE1A52A0489805C0DE9A1A469C844C7CABBEE625C3078888C1D85EEA883F1AD5BC4E6776E8E1A0750912DF64F79956499F1E182475B0B60E2632ADCD8CF94E9C54FD1F3B109D81F00BF2AB8CB862ADF7D40B9369A'
+ },
+ 'S-B': {
+ modulusLength: 1024,
+ p: '0xC6971FC57524B30C9018C5E621DE15499736854F56A6F8AEE65A7A404632B1BCF0349FFCAFCB0A103177971FC1612ADCDB8C8CC938C70225C8FD12AFF01B1D064E0AD6FDE6AB9159166CB9F2FC171D92F0CC7B6A6B2CD7FA342ACBE2C9315A42D576B1ECCE77A963157F3D0BD96A8EB0B0F3502AD238101B05116334F1E5B7AB',
+ q: '0xB09D634C10899CD7D4C3A7657403E05810B07C61A688BAB2C37F475E308B0607',
+ a: '0x3D26B467D94A3FFC9D71BF8DB8934084137264F3C2E9EB16DCA214B8BC7C872485336744934FD2EF5943F9ED0B745B90AA3EC8D70CDC91682478B664A2E1F8FB56CEF2972FEE7EDB084AF746419B854FAD02CC3E3646FF2E1A18DD4BEB3C44F7F2745588029649674546CC9187C207FB8F2CECE8E2293F68395C4704AF04BAB5'
+ },
+ 'S-C': {
+ modulusLength: 1024,
+ p: '0x9D88E6D7FE3313BD2E745C7CDD2AB9EE4AF3C8899E847DE74A33783EA68BC30588BA1F738C6AAF8AB350531F1854C3837CC3C860FFD7E2E106C3F63B3D8A4C034CE73942A6C3D585B599CF695ED7A3C4A93B2B947B7157BB1A1C043AB41EC8566C6145E938A611906DE0D32E562494569D7E999A0DDA5C879BDD91FE124DF1E9',
+ q: '0xFADD197ABD19A1B4653EECF7ECA4D6A22B1F7F893B641F901641FBB555354FAF',
+ a: '0x7447ED7156310599070B12609947A5C8C8A8625CF1CF252B407B331F93D639DDD1BA392656DECA992DD035354329A1E95A6E32D6F47882D960B8F10ACAFF796D13CD9611F853DAB6D2623483E46788708493937A1A29442598AEC2E0742022563440FE9C18740ECE6765AC05FAF024A64B026E7E408840819E962E7E5F401AE3'
+ },
+ 'S-D': {
+ modulusLength: 1024,
+ p: '0x80F102D32B0FD167D069C27A307ADAD2C466091904DBAA55D5B8CC7026F2F7A1919B890CB652C40E054E1E9306735B43D7B279EDDF9102001CD9E1A831FE8A163EED89AB07CF2ABE8242AC9DEDDDBF98D62CDDD1EA4F5F15D3A42A6677BDD293B24260C0F27C0F1D15948614D567B66FA902BAA11A69AE3BCEADBB83E399C9B5',
+ q: '0xF0F544C418AAC234F683F033511B65C21651A6078BDA2D69BB9F732867502149',
+ a: '0x6BCC0B4FADB3889C1E06ADD23CC09B8AB6ECDEDF73F04632595EE4250005D6AF5F5ADE44CB1E26E6263C672347CFA26F9E9393681E6B759733784CDE5DBD9A14A39369DFD99FA85CC0D10241C4010343F34A91393A706CF12677CBFA1F578D6B6CFBE8A1242CFCC94B3B653A476E145E3862C18CC3FED8257CFEF74CDB205BF1'
+ },
+ 'X-A': {
+ modulusLength: 1024,
+ p: '0xCA3B3F2EEE9FD46317D49595A9E7518E6C63D8F4EB4D22D10D28AF0B8839F079F8289E603B03530784B9BB5A1E76859E4850C670C7B71C0DF84CA3E0D6C177FE9F78A9D8433230A883CD82A2B2B5C7A3306980278570CDB79BF01074A69C9623348824B0C53791D53C6A78CAB69E1CFB28368611A397F50F541E16DB348DBE5F',
+ q: '0xCAE4D85F80C147704B0CA48E85FB00A9057AA4ACC44668E17F1996D7152690D9',
+ a: '0xBE27D652F2F1E339DA734211B85B06AE4DE236AA8FBEEB3F1ADCC52CD43853777E834A6A518138678A8ADBD3A55C70A7EAB1BA7A0719548677AAF4E609FFB47F6B9D7E45B0D06D83D7ADC53310ABD85783E7317F7EC73268B6A9C08D260B85D8485696CA39C17B17F044D1E050489036ABD381C5E6BF82BA352A1AFF136601AF'
+ },
+ 'X-B': {
+ modulusLength: 1024,
+ p: '0x9286DBDA91ECCFC3060AA5598318E2A639F5BA90A4CA656157B2673FB191CD0589EE05F4CEF1BD13508408271458C30851CE7A4EF534742BFB11F4743C8F787B11193BA304C0E6BCA25701BF88AF1CB9B8FD4711D89F88E32B37D95316541BF1E5DBB4989B3DF13659B88C0F97A3C1087B9F2D5317D557DCD4AFC6D0A754E279',
+ q: '0xC966E9B3B8B7CDD82FF0F83AF87036C38F42238EC50A876CD390E43D67B6013F',
+ a: '0x7E9C3096676F51E3B2F9884CF0AC2156779496F410E049CED7E53D8B7B5B366B1A6008E5196605A55E89C3190DABF80B9F1163C979FCD18328DAE5E9048811B370107BB7715F82091BB9DE0E33EE2FED6255474F8769FCE5EAFAEEF1CB5A32E0D5C6C2F0FC0B3447072947F5B4C387666993A333FC06568E534AD56D2338D729'
+ },
+ 'X-C': {
+ modulusLength: 1024,
+ p: '0xB194036ACE14139D36D64295AE6C50FC4B7D65D8B340711366CA93F383653908EE637BE428051D86612670AD7B402C09B820FA77D9DA29C8111A8496DA6C261A53ED252E4D8A69A20376E6ADDB3BDCD331749A491A184B8FDA6D84C31CF05F9119B5ED35246EA4562D85928BA1136A8D0E5A7E5C764BA8902029A1336C631A1D',
+ q: '0x96120477DF0F3896628E6F4A88D83C93204C210FF262BCCB7DAE450355125259',
+ a: '0x3F1817052BAA7598FE3E4F4FC5C5F616E122CFF9EBD89EF81DC7CE8BF56CC64B43586C80F1C4F56DD5718FDD76300BE336784259CA25AADE5A483F64C02A20CF4A10F9C189C433DEFE31D263E6C9764660A731ECCAECB74C8279303731E8CF69205BC73E5A70BDF93E5BB681DAB4EEB9C733CAAB2F673C475E0ECA921D29782E'
+ }
+}; //
+
+/*
+ * BigInteger arithmetic tools
+ * optimized release of http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js
+ *
+ */ //
+
+// Bits per one element
+var DB = 28, DM = (1 << DB) - 1, DV = 1 << DB,
+ FV = Math.pow(2, 52), F1 = 52 - DB, F2 = 2 * DB - 52;
+
+function am(y, i, x, w, j, c, n) {
+ var xl = x & 0x3fff, xh = x >> 14;
+ while (--n >= 0) {
+ var l = y[i] & 0x3fff;
+ var h = y[i++] >> 14;
+ var m = xh * l + h * xl;
+ l = xl * l + ((m & 0x3fff) << 14) + w[j] + c;
+ c = (l >> 28) + (m >> 14) + xh * h;
+ w[j++] = l & 0xfffffff;
+ }
+ return c;
+}
+
+function nbi(words) {
+ var r = new Array(Math.ceil(words));
+ r.s = 0;
+ r.t = 0;
+ return r;
+}
+
+function copyTo(x, r) {
+ for (var i = x.t - 1; i >= 0; --i)
+ r[i] = x[i];
+ r.t = x.t;
+ r.s = x.s;
+ return r;
+}
+
+function copy(x) {
+ return copyTo(x, nbi(x.t));
+}
+
+function setInt(x, i) {
+ x.t = 1;
+ x.s = (i < 0) ? -1 : 0;
+ if (i > 0)
+ x[0] = i;
+ else if (i < -1)
+ x[0] = i + DV;
+ else
+ x.t = 0;
+ return x;
+}
+
+function nbv(i) {
+ var r = nbi(1);
+ setInt(r, i);
+ return r;
+}
+
+var ZERO = nbv(0), ONE = nbv(1), THREE = nbv(3);
+
+function clamp(x) {
+ var c = x.s & DM;
+ while (x.t > 0 && x[x.t - 1] === c)
+ --x.t;
+ return x;
+}
+
+function subTo(x, a, r) {
+ var i = 0, c = 0, m = Math.min(a.t, x.t);
+ while (i < m) {
+ c += x[i] - a[i];
+ r[i++] = c & DM;
+ c >>= DB;
+ }
+ if (a.t < x.t) {
+ c -= a.s;
+ while (i < x.t) {
+ c += x[i];
+ r[i++] = c & DM;
+ c >>= DB;
+ }
+ c += x.s;
+ }
+ else {
+ c += x.s;
+ while (i < a.t) {
+ c -= a[i];
+ r[i++] = c & DM;
+ c >>= DB;
+ }
+ c -= a.s;
+ }
+ r.s = (c < 0) ? -1 : 0;
+ if (c < -1)
+ r[i++] = DV + c;
+ else if (c > 0)
+ r[i++] = c;
+ r.t = i;
+ return clamp(r);
+}
+
+function sub(x, y) {
+ return subTo(x, y, nbi(x.t));
+}
+
+function addTo(x, a, r) {
+ var i = 0, c = 0, m = Math.min(a.t, x.t);
+ while (i < m) {
+ c += x[i] + a[i];
+ r[i++] = c & DM;
+ c >>= DB;
+ }
+ if (a.t < x.t) {
+ c += a.s;
+ while (i < x.t) {
+ c += x[i];
+ r[i++] = c & DM;
+ c >>= DB;
+ }
+ c += x.s;
+ }
+ else {
+ c += x.s;
+ while (i < a.t) {
+ c += a[i];
+ r[i++] = c & DM;
+ c = c >> DB;
+ }
+ c += a.s;
+ }
+ r.s = (c < 0) ? -1 : 0;
+ if (c > 0)
+ r[i++] = c;
+ else if (c < -1)
+ r[i++] = DV + c;
+ r.t = i;
+ return clamp(r);
+}
+
+function add(x, y) {
+ return addTo(x, y, nbi(x.t));
+}
+
+function negTo(x, r) {
+ return subTo(ZERO, x, r);
+}
+
+function neg(x) {
+ return negTo(x, nbi(x.t));
+}
+
+function absTo(x, r) {
+ return (x.s < 0) ? negTo(r) : copyTo(r);
+}
+
+function abs(x) {
+ return (x.s < 0) ? neg(x) : x;
+}
+
+function compare(x, a) {
+ var r = x.s - a.s;
+ if (r !== 0)
+ return r;
+ var i = x.t;
+ r = i - a.t;
+ if (r !== 0)
+ return (x.s < 0) ? -r : r;
+ while (--i >= 0)
+ if ((r = x[i] - a[i]) !== 0)
+ return r;
+ return 0;
+}
+
+function equals(x, y) {
+ return(compare(x, y) === 0);
+}
+
+function min(x, y) {
+ return(compare(x, y) < 0) ? x : y;
+}
+
+function max(x, y) {
+ return(compare(x, y) > 0) ? x : y;
+}
+
+function nbits(x) {
+ var r = 1, t;
+ if ((t = x >>> 16) !== 0) {
+ x = t;
+ r += 16;
+ }
+ if ((t = x >> 8) !== 0) {
+ x = t;
+ r += 8;
+ }
+ if ((t = x >> 4) !== 0) {
+ x = t;
+ r += 4;
+ }
+ if ((t = x >> 2) !== 0) {
+ x = t;
+ r += 2;
+ }
+ if ((t = x >> 1) !== 0) {
+ x = t;
+ r += 1;
+ }
+ return r;
+}
+
+function dshlTo(x, n, r) {
+ var i;
+ for (i = x.t - 1; i >= 0; --i)
+ r[i + n] = x[i];
+ for (i = n - 1; i >= 0; --i)
+ r[i] = 0;
+ r.t = x.t + n;
+ r.s = x.s;
+ return r;
+}
+function dshrTo(x, n, r) {
+ for (var i = n; i < x.t; ++i)
+ r[i - n] = x[i];
+ r.t = Math.max(x.t - n, 0);
+ r.s = x.s;
+ return r;
+}
+
+function shlTo(x, n, r) {
+ var bs = n % DB;
+ var cbs = DB - bs;
+ var bm = (1 << cbs) - 1;
+ var ds = Math.floor(n / DB), c = (x.s << bs) & DM, i;
+ for (i = x.t - 1; i >= 0; --i) {
+ r[i + ds + 1] = (x[i] >> cbs) | c;
+ c = (x[i] & bm) << bs;
+ }
+ for (i = ds - 1; i >= 0; --i)
+ r[i] = 0;
+ r[ds] = c;
+ r.t = x.t + ds + 1;
+ r.s = x.s;
+ return clamp(r);
+}
+
+function shrTo(x, n, r) {
+ r.s = x.s;
+ var ds = Math.floor(n / DB);
+ if (ds >= x.t) {
+ r.t = 0;
+ return;
+ }
+ var bs = n % DB;
+ var cbs = DB - bs;
+ var bm = (1 << bs) - 1;
+ r[0] = x[ds] >> bs;
+ for (var i = ds + 1; i < x.t; ++i) {
+ r[i - ds - 1] |= (x[i] & bm) << cbs;
+ r[i - ds] = x[i] >> bs;
+ }
+ if (bs > 0)
+ r[x.t - ds - 1] |= (x.s & bm) << cbs;
+ r.t = x.t - ds;
+ return clamp(r);
+}
+
+function shl(x, n) {
+ var r = nbi(x.t);
+ if (n < 0)
+ shrTo(x, -n, r);
+ else
+ shlTo(x, n, r);
+ return r;
+}
+
+function shr(x, n) {
+ var r = nbi(x.t);
+ if (n < 0)
+ shlTo(x, -n, r);
+ else
+ shrTo(x, n, r);
+ return r;
+}
+
+function bitLength(x) {
+ if (x.t <= 0)
+ return 0;
+ return DB * (x.t - 1) + nbits(x[x.t - 1] ^ (x.s & DM));
+}
+
+function mulTo(b, a, r) {
+ var x = abs(b), y = abs(a);
+ var i = x.t;
+ r.t = i + y.t;
+ while (--i >= 0)
+ r[i] = 0;
+ for (i = 0; i < y.t; ++i)
+ r[i + x.t] = am(x, 0, y[i], r, i, 0, x.t);
+ r.s = 0;
+ if (b.s !== a.s)
+ subTo(ZERO, r, r);
+ return clamp(r);
+}
+
+function mul(x, y) {
+ return mulTo(x, y, nbi(x.t + y.t));
+}
+
+function sqrTo(a, r) {
+ var x = abs(a);
+ var i = r.t = 2 * x.t;
+ while (--i >= 0)
+ r[i] = 0;
+ for (i = 0; i < x.t - 1; ++i) {
+ var c = am(x, i, x[i], r, 2 * i, 0, 1);
+ if ((r[i + x.t] += am(x, i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) {
+ r[i + x.t] -= x.DV;
+ r[i + x.t + 1] = 1;
+ }
+ }
+ if (r.t > 0)
+ r[r.t - 1] += am(x, i, x[i], r, 2 * i, 0, 1);
+ r.s = 0;
+ return clamp(r);
+}
+
+function sqr(a) {
+ return sqrTo(a, nbi(a.t * 2));
+}
+
+function divRemTo(n, m, q, r) {
+ var pm = abs(m);
+ if (pm.t <= 0)
+ throw new OperationError('Division by zero');
+ var pt = abs(n);
+ if (pt.t < pm.t) {
+ if (q)
+ setInt(q, 0);
+ if (r)
+ copyTo(n, r);
+ return q;
+ }
+ if (!r)
+ r = nbi(m.t);
+ var y = nbi(m.t), ts = n.s, ms = m.s;
+ var nsh = DB - nbits(pm[pm.t - 1]);
+ if (nsh > 0) {
+ shlTo(pm, nsh, y);
+ shlTo(pt, nsh, r);
+ }
+ else {
+ copyTo(pm, y);
+ copyTo(pt, r);
+ }
+ var ys = y.t;
+ var y0 = y[ys - 1];
+ if (y0 === 0)
+ return q;
+ var yt = y0 * (1 << F1) + ((ys > 1) ? y[ys - 2] >> F2 : 0);
+ var d1 = FV / yt, d2 = (1 << F1) / yt, e = 1 << F2;
+ var i = r.t, j = i - ys, t = !q ? nbi(Math.max(n.t - m.t, 1)) : q;
+ dshlTo(y, j, t);
+ if (compare(r, t) >= 0) {
+ r[r.t++] = 1;
+ subTo(r, t, r);
+ }
+ dshlTo(ONE, ys, t);
+ subTo(t, y, y);
+ while (y.t < ys)
+ y[y.t++] = 0;
+ while (--j >= 0) {
+ var qd = (r[--i] === y0) ? DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2);
+ if ((r[i] += am(y, 0, qd, r, j, 0, ys)) < qd) {
+ dshlTo(y, j, t);
+ subTo(r, t, r);
+ while (r[i] < --qd)
+ subTo(r, t, r);
+ }
+ }
+ if (q) {
+ dshrTo(r, ys, q);
+ if (ts !== ms)
+ subTo(ZERO, q, q);
+ }
+ r.t = ys;
+ clamp(r);
+ if (nsh > 0)
+ shrTo(r, nsh, r);
+ if (ts < 0)
+ subTo(ZERO, r, r);
+ return q;
+}
+
+function modTo(b, a, r) {
+ divRemTo(abs(b), a, null, r);
+ if (b.s < 0 && compare(r, ZERO) > 0)
+ subTo(a, r, r);
+ return r;
+}
+
+function mod(b, a) {
+ return modTo(b, a, nbi(a.t));
+}
+
+function div(b, a) {
+ return divRemTo(b, a, nbi(Math.max(b.t - a.t, 1)), null);
+}
+
+function isEven(x) {
+
+ return ((x.t > 0) ? (x[0] & 1) : x.s) === 0;
+}
+
+function isZero(x) {
+ return equals(x, ZERO);
+}
+
+function sig(x) {
+ if (x.s < 0)
+ return -1;
+ else if (x.t <= 0 || (x.t === 1 && x[0] <= 0))
+ return 0;
+ else
+ return 1;
+}
+
+function invMod(x, m) {
+ var ac = isEven(m);
+ if ((isEven(x) && ac) || sig(m) === 0)
+ return ZERO;
+ var u = copy(m), v = copy(x);
+ var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
+ while (sig(u) !== 0) {
+ while (isEven(u)) {
+ shrTo(u, 1, u);
+ if (ac) {
+ if (!isEven(a) || !isEven(b)) {
+ addTo(a, x, a);
+ subTo(b, m, b);
+ }
+ shrTo(a, 1, a);
+ }
+ else if (!isEven(b))
+ subTo(b, m, b);
+ shrTo(b, 1, b);
+ }
+ while (isEven(v)) {
+ shrTo(v, 1, v);
+ if (ac) {
+ if (!isEven(c) || !isEven(d)) {
+ addTo(c, x, c);
+ subTo(d, m, d);
+ }
+ shrTo(c, 1, c);
+ }
+ else if (!isEven(d))
+ subTo(d, m, d);
+ shrTo(d, 1, d);
+ }
+ if (compare(u, v) >= 0) {
+ subTo(u, v, u);
+ if (ac)
+ subTo(a, c, a);
+ subTo(b, d, b);
+ }
+ else {
+ subTo(v, u, v);
+ if (ac)
+ subTo(c, a, c);
+ subTo(d, b, d);
+ }
+ }
+ if (compare(v, ONE) !== 0)
+ return ZERO;
+ if (compare(d, m) >= 0)
+ return subtract(d, m);
+ if (sig(d) < 0)
+ addTo(d, m, d);
+ else
+ return d;
+ if (sig(d) < 0)
+ return add(d, m);
+ else
+ return d;
+}
+
+function testBit(x, n) {
+ var j = Math.floor(n / DB);
+ if (j >= x.t)
+ return (x.s !== 0);
+ return ((x[j] & (1 << (n % DB))) !== 0);
+}
+
+function nothing(x) {
+ return x;
+}
+
+function extend(c, o) {
+ for (var i in o)
+ c.prototype[i] = o[i];
+} //
+
+/*
+ * Classic, Barret, Mongomery reductions, optimized ExpMod algorithms
+ * optimized release of http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn2.js
+ *
+ */ //
+
+// Classic reduction
+var Classic = function (m) {
+ this.m = m;
+};
+
+extend(Classic, {
+ convert: function (x) {
+ if (x.s < 0 || compare(x, this.m) >= 0)
+ return mod(x, this.m);
+ else
+ return x;
+ },
+ revert: nothing,
+ reduce: function (x) {
+ modTo(x, this.m, x);
+ },
+ sqrTo: function (x, r) {
+ sqrTo(x, r);
+ this.reduce(r);
+ },
+ mulTo: function (x, y, r) {
+ mulTo(x, y, r);
+ this.reduce(r);
+ }
+});
+
+function invDig(a) {
+ if (a.t < 1)
+ return 0;
+ var x = a[0];
+ if ((x & 1) === 0)
+ return 0;
+ var y = x & 3;
+ y = (y * (2 - (x & 0xf) * y)) & 0xf;
+ y = (y * (2 - (x & 0xff) * y)) & 0xff;
+ y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff;
+ y = (y * (2 - x * y % DV)) % DV;
+ return (y > 0) ? DV - y : -y;
+}
+
+// Montgomery reduction
+var Montgomery = function (m) {
+ this.m = m;
+ this.mp = invDig(m);
+ this.mpl = this.mp & 0x7fff;
+ this.mph = this.mp >> 15;
+ this.um = (1 << (DB - 15)) - 1;
+ this.mt2 = 2 * m.t;
+};
+
+extend(Montgomery, {
+ // xR mod m
+ convert: function (x) {
+ var r = nbi(x.t);
+ dshlTo(abs(x), this.m.t, r);
+ divRemTo(r, this.m, null, r);
+ if (x.s < 0 && compare(r, ZERO) > 0)
+ subTo(this.m, r, r);
+ return r;
+ },
+ // x/R mod m
+ revert: function (x) {
+ var r = nbi(x.t);
+ copyTo(x, r);
+ this.reduce(r);
+ return r;
+ },
+ // x = x/R mod m (HAC 14.32)
+ reduce: function (x) {
+ while (x.t <= this.mt2)
+ x[x.t++] = 0;
+ for (var i = 0; i < this.m.t; ++i) {
+ var j = x[i] & 0x7fff;
+ var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & DM;
+ j = i + this.m.t;
+ x[j] += am(this.m, 0, u0, x, i, 0, this.m.t);
+ while (x[j] >= DV) {
+ x[j] -= DV;
+ x[++j]++;
+ }
+ }
+ clamp(x);
+ dshrTo(x, this.m.t, x);
+ if (compare(x, this.m) >= 0)
+ subTo(x, this.m, x);
+ },
+ // r = "x^2/R mod m"; x != r
+ sqrTo: function (x, r) {
+ sqrTo(x, r);
+ this.reduce(r);
+ },
+ // r = "xy/R mod m"; x,y != r
+ mulTo: function (x, y, r) {
+ mulTo(x, y, r);
+ this.reduce(r);
+ }
+});
+
+function dAddOffset(x, n, w) {
+ if (n === 0)
+ return;
+ while (x.t <= w)
+ x[x.t++] = 0;
+ x[w] += n;
+ while (x[w] >= DV) {
+ x[w] -= DV;
+ if (++w >= x.t)
+ x[x.t++] = 0;
+ ++x[w];
+ }
+}
+
+function mulLowerTo(x, a, n, r) {
+ var i = Math.min(x.t + a.t, n);
+ r.s = 0; // assumes a,x >= 0
+ r.t = i;
+ while (i > 0)
+ r[--i] = 0;
+ var j;
+ for (j = r.t - x.t; i < j; ++i)
+ r[i + x.t] = am(x, 0, a[i], r, i, 0, x.t);
+ for (j = Math.min(a.t, n); i < j; ++i)
+ am(x, 0, a[i], r, i, 0, n - i);
+ return clamp(r);
+}
+
+function mulUpperTo(x, a, n, r) {
+ --n;
+ var i = r.t = x.t + a.t - n;
+ r.s = 0; // assumes a,x >= 0
+ while (--i >= 0)
+ r[i] = 0;
+ for (i = Math.max(n - x.t, 0); i < a.t; ++i)
+ r[x.t + i - n] = am(x, n - i, a[i], r, 0, 0, x.t + i - n);
+ clamp(r);
+ return dshrTo(r, 1, r);
+}
+
+// Barrett modular reduction
+function Barrett(m) {
+ // setup Barrett
+ this.r2 = nbi(2 * m.t);
+ this.q3 = nbi(2 * m.t);
+ dshlTo(ONE, 2 * m.t, this.r2);
+ this.mu = div(this.r2, m);
+ this.m = m;
+}
+
+extend(Barrett, {
+ convert: function (x) {
+ if (x.s < 0 || x.t > 2 * this.m.t)
+ return mod(x, this.m);
+ else if (compare(x, this.m) < 0)
+ return x;
+ else {
+ var r = nbi(x.t);
+ copyTo(x, r);
+ this.reduce(r);
+ return r;
+ }
+ },
+ revert: function (x) {
+ return x;
+ },
+ // x = x mod m (HAC 14.42)
+ reduce: function (x) {
+ dshrTo(x, this.m.t - 1, this.r2);
+ if (x.t > this.m.t + 1) {
+ x.t = this.m.t + 1;
+ clamp(x);
+ }
+ mulUpperTo(this.mu, this.r2, this.m.t + 1, this.q3);
+ mulLowerTo(this.m, this.q3, this.m.t + 1, this.r2);
+ while (compare(x, this.r2) < 0)
+ dAddOffset(x, 1, this.m.t + 1);
+ subTo(x, this.r2, x);
+ while (compare(x, this.m) >= 0)
+ subTo(x, this.m, x);
+ },
+ // r = x^2 mod m; x != r
+ sqrTo: function (x, r) {
+ sqrTo(x, r);
+ this.reduce(r);
+ },
+ // r = x*y mod m; x,y != r
+ mulTo: function (x, y, r) {
+ mulTo(x, y, r);
+ this.reduce(r);
+ }
+
+});
+
+// x^e % m (HAC 14.85)
+function expMod(x, e, m) {
+ var i = bitLength(e), k, r = nbv(1), z;
+ if (i <= 0)
+ return r;
+ else if (i < 18)
+ k = 1;
+ else if (i < 48)
+ k = 3;
+ else if (i < 144)
+ k = 4;
+ else if (i < 768)
+ k = 5;
+ else
+ k = 6;
+ if (i < 8)
+ z = new Classic(m);
+ else if (isEven(m))
+ z = new Barrett(m);
+ else
+ z = new Montgomery(m);
+
+ // precomputation
+ var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1;
+ g[1] = z.convert(x);
+ if (k > 1) {
+ var g2 = nbi(m.t * 2);
+ z.sqrTo(g[1], g2);
+ while (n <= km) {
+ g[n] = nbi(m.t * 2);
+ z.mulTo(g2, g[n - 2], g[n]);
+ n += 2;
+ }
+ }
+
+ var j = e.t - 1, w, is1 = true, r2 = nbi(m.t * 2), t;
+ i = nbits(e[j]) - 1;
+ while (j >= 0) {
+ if (i >= k1)
+ w = (e[j] >> (i - k1)) & km;
+ else {
+ w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i);
+ if (j > 0)
+ w |= e[j - 1] >> (DB + i - k1);
+ }
+
+ n = k;
+ while ((w & 1) == 0) {
+ w >>= 1;
+ --n;
+ }
+ if ((i -= n) < 0) {
+ i += DB;
+ --j;
+ }
+ if (is1) { // ret == 1, don't bother squaring or multiplying it
+ copyTo(g[w], r);
+ is1 = false;
+ }
+ else {
+ while (n > 1) {
+ z.sqrTo(r, r2);
+ z.sqrTo(r2, r);
+ n -= 2;
+ }
+ if (n > 0)
+ z.sqrTo(r, r2);
+ else {
+ t = r;
+ r = r2;
+ r2 = t;
+ }
+ z.mulTo(r2, g[w], r);
+ }
+ while (j >= 0 && (e[j] & (1 << i)) == 0) {
+ z.sqrTo(r, r2);
+ t = r;
+ r = r2;
+ r2 = t;
+ if (--i < 0) {
+ i = DB - 1;
+ --j;
+ }
+ }
+ }
+ return z.revert(r);
+} //
+
+/*
+ * EC Field Elements, Points, Curves
+ * optimized release of http://www-cs-students.stanford.edu/~tjw/jsbn/ec.js
+ *
+ */ //
+
+// EC Field Elemets
+function newFE(a, x) {
+ a.r.reduce(x);
+ x.q = a.q;
+ x.r = a.r;
+ return x;
+}
+
+function copyFE(a, x) {
+ x.q = a.q;
+ x.r = a.r;
+ return x;
+}
+
+function negFE(a) {
+ return copyFE(a, sub(a.q, a));
+}
+
+function addFE(a, b) {
+ var r = add(a, b);
+ if (compare(r, a.q) > 0)
+ subTo(r, a.q, r);
+ return copyFE(a, r);
+}
+
+function subFE(a, b) {
+ var r = sub(a, b);
+ if (r.s < 0)
+ addTo(a.q, r, r);
+ return copyFE(a, r);
+}
+
+function mulFE(a, b) {
+ return newFE(a, mul(a, b));
+}
+
+function sqrFE(a) {
+ return newFE(a, sqr(a));
+}
+
+function shlFE(a, i) {
+ return newFE(a, shl(a, i));
+}
+
+function invFE(a) {
+ return copyFE(a, invMod(a, a.q));
+}
+
+// EC Points
+function newEC(curve, x, y, z) {
+ return {
+ curve: curve,
+ x: x,
+ y: y,
+ z: z || newFE(curve, ONE)
+ };
+}
+
+function getX(point) {
+ if (!point.zinv)
+ point.zinv = invFE(point.z);
+ return mulFE(point.x, point.zinv);
+}
+
+function getY(point) {
+ if (!point.zinv)
+ point.zinv = invFE(point.z);
+ return mulFE(point.y, point.zinv);
+}
+
+function isInfinity(a) {
+ if ((!a.x) && (!a.y))
+ return true;
+ return isZero(a.z) && !isZero(a.y);
+}
+
+function getInfinity(a) {
+ return a.curve.infinity;
+}
+
+function equalsEC(a, b) {
+ if (a === b)
+ return true;
+ if (isInfinity(a))
+ return isInfinity(b);
+ if (isInfinity(b))
+ return isInfinity(a);
+ var u, v;
+ // u = Y2 * Z1 - Y1 * Z2
+ u = subFE(mulFE(b.y, a.z), mulFE(a.y, b.z));
+ if (!isZero(u))
+ return false;
+ // v = X2 * Z1 - X1 * Z2
+ v = subFE(mulFE(b.x, a.z), mulFE(a.x, b.z));
+ return isZero(v);
+}
+
+function negEC(a) {
+ return newEC(a.curve, a.x, negFE(a.y), a.z);
+}
+
+function addEC(a, b) {
+ if (isInfinity(a))
+ return b;
+ if (isInfinity(b))
+ return a;
+
+ // u = Y2 * Z1 - Y1 * Z2
+ var u = subFE(mulFE(b.y, a.z), mulFE(a.y, b.z));
+ // v = X2 * Z1 - X1 * Z2
+ var v = subFE(mulFE(b.x, a.z), mulFE(a.x, b.z));
+
+ if (isZero(v)) {
+ if (isZero(u)) {
+ return twiceEC(a); // a == b, so double
+ }
+ return getInfinity(a); // a = -b, so infinity
+ }
+
+ var x1 = a.x;
+ var y1 = a.y;
+
+ var v2 = sqrFE(v);
+ var v3 = mulFE(v2, v);
+ var x1v2 = mulFE(x1, v2);
+ var zu2 = mulFE(sqrFE(u), a.z);
+
+ // x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3)
+ var x3 = mulFE(subFE(mulFE(subFE(zu2, shlFE(x1v2, 1)), b.z), v3), v);
+ // y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3
+ var y3 = addFE(mulFE(subFE(subFE(mulFE(mulFE(x1v2, THREE), u), mulFE(y1, v3)), mulFE(zu2, u)), b.z), mulFE(u, v3));
+ // z3 = v^3 * z1 * z2
+ var z3 = mulFE(mulFE(v3, a.z), b.z);
+
+ return newEC(a.curve, x3, y3, z3);
+}
+
+function twiceEC(b) {
+ if (isInfinity(b))
+ return b;
+ if (sig(b.y) === 0)
+ return getInfinity(b);
+
+ var x1 = b.x;
+ var y1 = b.y;
+
+ var y1z1 = mulFE(y1, b.z);
+ var y1sqz1 = mulFE(y1z1, y1);
+ var a = b.curve.a;
+
+ // w = 3 * x1^2 + a * z1^2
+ var w = mulFE(sqrFE(x1), THREE);
+ if (!isZero(a)) {
+ w = addFE(w, mulFE(sqrFE(b.z), a));
+ }
+
+ // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1)
+ var x3 = mulFE(shlFE(subFE(sqrFE(w), mulFE(shlFE(x1, 3), y1sqz1)), 1), y1z1);
+ // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3
+ var y3 = subFE(mulFE(shlFE(subFE(mulFE(mulFE(w, THREE), x1), shlFE(y1sqz1, 1)), 2), y1sqz1), mulFE(sqrFE(w), w));
+ // z3 = 8 * (y1 * z1)^3
+ var z3 = shlFE(mulFE(sqrFE(y1z1), y1z1), 3);
+
+ return newEC(b.curve, x3, y3, z3);
+}
+
+// Simple NAF (Non-Adjacent Form) multiplication algorithm
+function mulEC(a, k) {
+ if (isInfinity(a))
+ return a;
+ if (sig(k) === 0)
+ return getInfinity(a);
+
+ var e = k;
+ var h = mul(e, THREE);
+
+ var neg = negEC(a);
+ var R = a;
+
+ var i;
+ for (i = bitLength(h) - 2; i > 0; --i) {
+ R = twiceEC(R);
+
+ var hBit = testBit(h, i);
+ var eBit = testBit(e, i);
+
+ if (hBit !== eBit) {
+ R = addEC(R, hBit ? a : neg);
+ }
+ }
+
+ return R;
+}
+
+function mul2AndAddEC(a, k) {
+ var nbits = bitLength(k);
+ var R = a,
+ Q = getInfinity(a);
+
+ for (var i = 0; i < nbits - 1; i++) {
+ if (testBit(k, i) === 1)
+ Q = addEC(Q, R);
+
+ R = twiceEC(R);
+ }
+
+ if (testBit(k, nbits - 1) === 1)
+ Q = addEC(Q, R);
+
+ return Q;
+}
+
+// Compute a*j + x*k (simultaneous multiplication)
+function mulTwoEC(a, j, x, k) {
+ var i;
+ if (bitLength(j) > bitLength(k))
+ i = bitLength(j) - 1;
+ else
+ i = bitLength(k) - 1;
+
+ var R = getInfinity(a);
+ var both = addEC(a, x);
+ while (i >= 0) {
+ R = twiceEC(R);
+ if (testBit(j, i)) {
+ if (testBit(k, i)) {
+ R = addEC(R, both);
+ }
+ else {
+ R = addEC(R, a);
+ }
+ }
+ else {
+ if (testBit(k, i)) {
+ R = addEC(R, x);
+ }
+ }
+ --i;
+ }
+
+ return R;
+}
+
+// EC Curve
+function newCurve(q, a, b) {
+ var curve = {};
+ curve.q = q;
+ curve.r = new Barrett(q);
+ curve.a = newFE(curve, a);
+ curve.b = newFE(curve, b);
+ curve.infinity = newEC(curve);
+ return curve;
+} //
+
+/*
+ * Converion tools (hex, binary)
+ *
+ */ //
+
+function atobi(d) {
+ var k = 8;
+ var a = new Uint8Array(d);
+ var r = nbi(a.length * 8 / DB);
+ r.t = 0;
+ r.s = 0;
+ var sh = 0;
+ for (var i = 0, n = a.length; i < n; i++) {
+ var x = a[i];
+ if (sh === 0)
+ r[r.t++] = x;
+ else if (sh + k > DB) {
+ r[r.t - 1] |= (x & ((1 << (DB - sh)) - 1)) << sh;
+ r[r.t++] = (x >> (DB - sh));
+ }
+ else
+ r[r.t - 1] |= x << sh;
+ sh += k;
+ if (sh >= DB)
+ sh -= DB;
+ }
+ return clamp(r);
+}
+
+function bitoa(s, bitLength) {
+ var k = 8;
+ var km = (1 << k) - 1, d, m = false, r = [], i = s.t;
+ var p = DB - (i * DB) % k;
+ if (i-- > 0) {
+ if (p < DB && (d = s[i] >> p) > 0) {
+ m = true;
+ r.push(d);
+ }
+ while (i >= 0) {
+ if (p < k) {
+ d = (s[i] & ((1 << p) - 1)) << (k - p);
+ d |= s[--i] >> (p += DB - k);
+ }
+ else {
+ d = (s[i] >> (p -= k)) & km;
+ if (p <= 0) {
+ p += DB;
+ --i;
+ }
+ }
+ if (d > 0)
+ m = true;
+ if (m)
+ r.push(d);
+ }
+ }
+ var r8 = new Uint8Array(bitLength ? bitLength / 8 : r.length);
+ if (m)
+ r8.set(r.reverse());
+ return r8.buffer;
+}
+
+
+function htobi(s) {
+ if (typeof s === 'number' || s instanceof Number)
+ return nbv(s);
+ s = s.replace(/[^\-A-Fa-f0-9]/g, '');
+ if (!s)
+ s = '0';
+ var k = 4;
+ var r = nbi(s.length / 7);
+ var i = s.length, mi = false, sh = 0;
+ while (--i >= 0) {
+ var c = s.charAt(i);
+ if (c === '-') {
+ mi = true;
+ continue;
+ }
+ var x = parseInt(s.charAt(i), 16);
+ mi = false;
+ if (sh === 0)
+ r[r.t++] = x;
+ else if (sh + k > DB) {
+ r[r.t - 1] |= (x & ((1 << (DB - sh)) - 1)) << sh;
+ r[r.t++] = (x >> (DB - sh));
+ }
+ else
+ r[r.t - 1] |= x << sh;
+ sh += k;
+ if (sh >= DB)
+ sh -= DB;
+ }
+ if (mi)
+ subTo(ZERO, r, r);
+ return clamp(r);
+}
+
+function bitoh(x) {
+ if (x.s < 0)
+ return "-" + bitoh(negTo(x, nbi(x.t)));
+ var k = 4;
+ var km = (1 << k) - 1, d, m = false, r = "", i = x.t;
+ var p = DB - (i * DB) % k;
+ if (i-- > 0) {
+ if (p < DB && (d = x[i] >> p) > 0) {
+ m = true;
+ r = d.toString(16);
+ }
+ while (i >= 0) {
+ if (p < k) {
+ d = (x[i] & ((1 << p) - 1)) << (k - p);
+ d |= x[--i] >> (p += DB - k);
+ }
+ else {
+ d = (x[i] >> (p -= k)) & km;
+ if (p <= 0) {
+ p += DB;
+ --i;
+ }
+ }
+ if (d > 0)
+ m = true;
+ if (m)
+ r += d.toString(16);
+ }
+ }
+ return "0x" + (m ? r : "0");
+}
+
+// biginteger to big-endian integer bytearray
+function bitoi(s) {
+ var i = s.t, r = [];
+ r[0] = s.s;
+ var p = DB - (i * DB) % 8, d, k = 0;
+ if (i-- > 0) {
+ if (p < DB && (d = s[i] >> p) !== (s.s & DM) >> p)
+ r[k++] = d | (s.s << (DB - p));
+ while (i >= 0) {
+ if (p < 8) {
+ d = (s[i] & ((1 << p) - 1)) << (8 - p);
+ d |= s[--i] >> (p += DB - 8);
+ }
+ else {
+ d = (s[i] >> (p -= 8)) & 0xff;
+ if (p <= 0) {
+ p += DB;
+ --i;
+ }
+ }
+ if ((d & 0x80) !== 0)
+ d |= -256;
+ if (k === 0 && (s.s & 0x80) !== (d & 0x80))
+ ++k;
+ if (k > 0 || d !== s.s)
+ r[k++] = d;
+ }
+ }
+ return new Uint8Array(r).buffer;
+}
+
+// big-endian integer bytearray to biginteger
+function itobi(d) {
+ var k = 8, s = new Uint8Array(d),
+ r = nbi(s.length / 7);
+ r.t = 0;
+ r.s = 0;
+ var i = s.length, sh = 0;
+ while (--i >= 0) {
+ var x = s[i] & 0xff;
+ if (sh === 0)
+ r[r.t++] = x;
+ else if (sh + k > DB) {
+ r[r.t - 1] |= (x & ((1 << (DB - sh)) - 1)) << sh;
+ r[r.t++] = (x >> (DB - sh));
+ }
+ else
+ r[r.t - 1] |= x << sh;
+ sh += k;
+ if (sh >= DB)
+ sh -= DB;
+ }
+ if ((s[0] & 0x80) !== 0) {
+ r.s = -1;
+ if (sh > 0)
+ r[r.t - 1] |= ((1 << (DB - sh)) - 1) << sh;
+ }
+ return clamp(r);
+}
+
+
+// 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;
+}
+
+// Calculate hash of data
+function hash(d) {
+ if (this.hash)
+ d = this.hash.digest(d);
+ // Swap hash for SignalCom
+ if (this.procreator === 'SC' ||
+ (this.procreator === 'VN' && this.hash.version === 2012))
+ d = swap(d);
+ return d;
+}
+
+// Check 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 or CryptoOperationDataView required');
+}
+
+// Check double buffer
+function to2(d) {
+ var b = buffer(d);
+ if (b.byteLength % 2 > 0)
+ throw new DataError('Buffer length must be even');
+ var n = b.byteLength / 2;
+ return [atobi(new Uint8Array(b, 0, n)), atobi(new Uint8Array(b, n, n))];
+}
+
+function from2(x, y, bitLength) {
+ var a = bitoa(x, bitLength),
+ b = bitoa(y, bitLength),
+ d = new Uint8Array(a.byteLength + b.byteLength);
+ d.set(new Uint8Array(a));
+ d.set(new Uint8Array(b), a.byteLength);
+ return d.buffer;
+}
+
+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');
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * The sign method returns sign data generated with the supplied privateKey.
+ *
+ * @memberOf GostSign
+ * @method sign
+ * @instance
+ * @param {(CryptoOperationData|TypedArray)} privateKey Private key
+ * @param {(CryptoOperationData|TypedArray)} data Data
+ * @returns {CryptoOperationData} Signature
+ */
+function sign(privateKey, data) //
+{
+
+ // Stage 1
+ var b = buffer(data);
+ var alpha = atobi(hash.call(this, b));
+
+ var q = this.q;
+ var x = mod(atobi(buffer(privateKey)), q);
+
+ // Stage 2
+ var e = mod(alpha, q);
+ if (isZero(e))
+ e = ONE;
+
+ var s = ZERO;
+ while (isZero(s)) {
+ var r = ZERO;
+ while (isZero(r)) {
+
+ // Stage 3
+ var k = mod(atobi(this.ukm ||
+ getSeed(this.bitLength)), q); // pseudo random 0 < k < q
+ // Stage 4
+ if (this.curve) {
+ // Gost R 34.10-2001 || Gost R 34.10-2012
+ var P = this.P;
+ var C = mulEC(P, k);
+ r = mod(getX(C), q);
+ } else {
+ // Gost R 34.10-94
+ var p = this.p, a = this.a;
+ r = mod(expMod(a, k, p), q);
+ }
+ }
+ // Stage 5
+ s = mod(add(mul(r, x), mul(k, e)), q);
+ }
+ // Stage 6
+ // console.log('s', bitoh(s));
+ // console.log('r', bitoh(r));
+ var zetta;
+ // Integer structure for SignalCom algorithm
+ if (this.procreator === 'SC') {
+ zetta = {
+ r: bitoh(r),
+ s: bitoh(s)
+ };
+ } else {
+ zetta = from2(r, s, this.bitLength);
+ // Swap bytes for CryptoPro algorithm
+ if (this.procreator === 'CP' || this.procreator === 'VN')
+ zetta = swap(zetta);
+ }
+ return zetta;
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * The verify method returns signature verification for the supplied publicKey.
+ *
+ * @memberOf GostSign
+ * @method sign
+ * @instance
+ * @param {(CryptoOperationData|TypedArray)} publicKey Public key
+ * @param {(CryptoOperationData|TypedArray)} signature Signature
+ * @param {(CryptoOperationData|TypedArray)} data Data
+ * @returns {boolean} Signature verified = true
+ */
+function verify(publicKey, signature, data) //
+{
+
+ // Stage 1
+ var q = this.q;
+ var r, s;
+ // Ready int for SignalCom algorithm
+ if (this.procreator === 'SC') {
+ r = htobi(signature.r);
+ s = htobi(signature.s);
+ } else {
+ if (this.procreator === 'CP' || this.procreator === 'VN')
+ signature = swap(signature);
+ var zetta = to2(signature);
+ // Swap bytes for CryptoPro algorithm
+ s = zetta[1]; // first 32 octets contain the big-endian representation of s
+ r = zetta[0]; // and second 32 octets contain the big-endian representation of r
+ }
+ if (compare(r, q) >= 0 || compare(s, q) >= 0)
+ return false;
+ // Stage 2
+ var b = buffer(data);
+ var alpha = atobi(hash.call(this, b));
+ // Stage 3
+ var e = mod(alpha, q);
+ if (isZero(e) === 0)
+ e = ONE;
+ // Stage 4
+ var v = invMod(e, q);
+ // Stage 5
+ var z1 = mod(mul(s, v), q);
+ var z2 = sub(q, mod(mul(r, v), q));
+ // Stage 6
+ if (this.curve) {
+ // Gost R 34.10-2001 || Gost R 34.10-2012
+ var k2 = to2(publicKey),
+ curve = this.curve,
+ P = this.P,
+ x = newFE(curve, k2[0]), // first 32 octets contain the little-endian representation of x
+ y = newFE(curve, k2[1]), // and second 32 octets contain the little-endian representation of y.
+ Q = new newEC(curve, x, y); // This corresponds to the binary representation of (256||256)
+ var C = mulTwoEC(P, z1, Q, z2);
+ var R = mod(getX(C), q);
+ } else {
+ // Gost R 34.10-94
+ var p = this.p, a = this.a;
+ var y = atobi(publicKey);
+ var R = mod(mod(mul(expMod(a, z1, p), expMod(y, z2, p)), p), q);
+ }
+ // Stage 7
+ return (compare(R, r) === 0);
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * The generateKey method returns a new generated key pair using the specified
+ * AlgorithmIdentifier.
+ *
+ * @memberOf GostSign
+ * @method generateKey
+ * @instance
+ * @returns {Object} Object with two CryptoOperationData members: privateKey and publicKey
+ */
+function generateKey() //
+{
+ var curve = this.curve;
+ if (curve) {
+
+ var Q = curve.infinity;
+ while (isInfinity(Q)) {
+
+ // Generate random private key
+ var d = ZERO;
+ if (this.ukm) {
+ d = atobi(this.ukm);
+ } else {
+ while (isZero(d))
+ d = mod(atobi(getSeed(this.bitLength)), this.q); // 0 < d < q
+ }
+
+ // Calculate public key
+ Q = mulEC(this.P, d);
+ var x = getX(Q), y = getY(Q);
+ // console.log('d', bitoh(d));
+ // console.log('x', bitoh(x));
+ // console.log('y', bitoh(y));
+ }
+
+ // Return result
+ return {
+ privateKey: bitoa(d, this.bitLength),
+ publicKey: from2(x, y, this.bitLength) // This corresponds to the binary representation of (256||256)
+ };
+
+ } else
+ throw new NotSupportedError('Key generation for GOST R 34.10-94 not supported');
+} //
+
+/**
+ * Algorithm name GOST R 34.10 mode MASK
+ *
+ * The generateMaskKey method returns a new generated key mask using for wrapping.
+ *
+ * @memberOf GostSign
+ * @method generateMaskKey
+ * @instance
+ * @returns {Object} Object with two CryptoOperationData members: privateKey and publicKey
+ */
+function generateMaskKey() //
+{
+ var curve = this.curve;
+ if (curve) {
+ // Generate random private key
+ var d = ZERO;
+ while (isZero(d))
+ d = mod(atobi(getSeed(this.bitLength)), this.q); // 0 < d < q
+
+ // Return result
+ return bitoa(d, this.bitLength);
+ } else
+ throw new NotSupportedError('Key generation for GOST R 34.10-94 not supported');
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * Unwrap private key from private key and ukm (mask)
+ *
+ * @memberOf GostSign
+ * @method unwrap
+ * @instance
+ * @param {(CryptoOperationData|TypedArray)} baseKey Unwrapping key
+ * @param {(CryptoOperationData|TypedArray)} data Wrapped key
+ * @returns {Object} CryptoOperationData unwrapped privateKey
+ */
+function unwrapKey(baseKey, data) //
+{
+ var curve = this.curve;
+ if (curve) {
+ var q = this.q;
+ var x = mod(atobi(buffer(data)), q);
+ var y = mod(atobi(buffer(baseKey)), q);
+ var z = this.procreator === 'VN' ? mod(mul(x, y), q) : mod(mul(x, invMod(y, q)), q);
+ return bitoa(z);
+ } else
+ throw new NotSupportedError('Key wrapping GOST R 34.10-94 not supported');
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * Wrap private key with private key and ukm (mask)
+ *
+ * @memberOf GostSign
+ * @method unwrap
+ * @instance
+ * @param {(CryptoOperationData|TypedArray)} baseKey Wrapping key
+ * @param {(CryptoOperationData|TypedArray)} data Key
+ * @returns {Object} CryptoOperationData unwrapped privateKey
+ */
+function wrapKey(baseKey, data) //
+{
+ var curve = this.curve;
+ if (curve) {
+ var q = this.q;
+ var x = mod(atobi(buffer(data)), q);
+ var y = mod(atobi(buffer(baseKey)), q);
+ var z = this.procreator === 'VN' ? mod(mul(x, invMod(y, q)), q) : mod(mul(x, y), q);
+ return bitoa(z);
+ } else
+ throw new NotSupportedError('Key wrapping GOST R 34.10-94 not supported');
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * @memberOf GostSign
+ * @method derive
+ * @instance
+ * @private
+ * @param {CryptoOperationData} baseKey Key for deriviation
+ * @returns {CryptoOperationData}
+ */
+function derive(baseKey) //
+{
+
+ var k, ukm = atobi(this.ukm);
+ var q = this.q;
+ var x = mod(atobi(buffer(baseKey)), q);
+
+ if (this.curve) {
+ // 1) Let K(x,y,UKM) = ((UKM*x)(mod q)) . (y.P) (512 bit), where
+ // x - sender’s private key (256 bit)
+ // x.P - sender’s public key (512 bit)
+ // y - recipient’s private key (256 bit)
+ // y.P - recipient’s public key (512 bit)
+ // UKM - non-zero integer, produced as in step 2 p. 6.1 [GOSTR341001]
+ // P - base point on the elliptic curve (two 256-bit coordinates)
+ // UKM*x - x multiplied by UKM as integers
+ // x.P - a multiple point
+ var K = mulEC(this.peer_Q, mod(mul(ukm, x), q));
+ k = from2(getX(K), getY(K), // This corresponds to the binary representation of (256||256)
+ this.bitLength);
+ } else {
+ // 1) Let K(x,y) = a^(x*y) (mod p), where
+ // x - sender’s private key, a^x - sender’s public key
+ // y - recipient’s private key, a^y - recipient’s public key
+ // a, p - parameters
+ var p = this.p, a = this.a;
+ k = bitoa(expMod(this.peer_y, x, p));
+ }
+ // 2) Calculate a 256-bit hash of K(x,y,UKM):
+ // KEK(x,y,UKM) = gostSign (K(x,y,UKM)
+ return hash.call(this, k);
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * The deriveBits method returns length bits on baseKey.
+ *
+ * @memberOf GostSign
+ * @method deriveBits
+ * @instance
+ * @param {(CryptoOperationData|TypedArray)} baseKey Key for deriviation
+ * @param {number} length output bit-length
+ * @returns {CryptoOperationData} result
+ */
+function deriveBits(baseKey, length) //
+{
+ if (length < 8 || length > this.bitLength || length % 8 > 0)
+ throw new DataError('Length must be no more than ' + this.bitLength + ' bits and multiple of 8');
+ var n = length / 8,
+ b = derive.call(this, baseKey),
+ r = new Uint8Array(n);
+
+ r.set(new Uint8Array(b, 0, n));
+ return r.buffer;
+} //
+
+/**
+ * Algorithm name GOST R 34.10
+ *
+ * The deriveKey method returns 256 bit Key encryption key on baseKey.
+ *
+ * This algorithm creates a key encryption key (KEK) using 64 bit UKM,
+ * the sender’s private key, and the recipient’s public key (or the
+ * reverse of the latter pair
+ *
+ * @memberOf GostSign
+ * @method deriveKey
+ * @instance
+ * @param {(CryptoOperationData|TypedArray)} baseKey Key for deriviation
+ * @returns {CryptoOperationData} result
+ */
+function deriveKey(baseKey) //
+{
+ var b = derive.call(this, baseKey),
+ r = new Uint8Array(32);
+
+ r.set(new Uint8Array(b, 0, 32));
+ return r.buffer;
+} //
+
+
+/**
+ * Gost R 34.10 universal object
+ *
+ * References: {@link http://tools.ietf.org/html/rfc6986} and {@link http://tools.ietf.org/html/rfc5831}
+ *
+ * Normalized algorithm identifier common parameters:
+ *
+ *
+ * - name Algorithm name 'GOST R 34.10'
+ * - version Algorithm version
+ *
+ * - 1994 - Old-style GOST R 34.10-94 ExpMod algorithm with GOST R 34.11-94 hash
+ * - 2001 - GOST R 34.10-2001 Eliptic curve algorithm with old GOST R 34.11-94 hash
+ * - 2012 - GOST R 34.10-2012 Eliptic curve algorithm with GOST R 34.11-12 hash, default mode
+ *
+ *
+ * - length Length of hash and signature. Key length == hash length for EC algorithms and 2 * hash length for ExpMod algorithm
+ *
+ * - GOST R 34.10-256 - 256 bits digest, default mode
+ * - GOST R 34.10-512 - 512 bits digest only for GOST R 34.11-2012 hash
+ *
+ *
+ * - mode Algorithm mode
+ *
+ * - SIGN Digital signature mode (default)
+ * - DH Diffie-Hellman key generation and key agreement mode
+ *
+ *
+ * - sBox Paramset sBox for GOST 34.11-94. Used only if version = 1994 or 2001
+ *
+ *
+ * Supported algorithms, modes and parameters:
+ *
+ *
+ * - Sign/Verify mode (SIGN)
+ * - DeriveKey/DeriveBits mode (DH)
+ *
+ * - {@link CryptoOperationData} ukm User key material. Default - random generated value
+ * - {@link CryptoOperationData} public The peer's EC public key data
+ *
+ *
+ * - GenerateKey mode (SIGN and DH) version = 1994
+ *
+ * - namedParam Paramset for key generation algorithm. If specified no additianal parameters required
+ *
+ * Additional parameters, if namedParam not specified
+ *
+ * - modulusLength Bit length of p (512 or 1024 bits). Default = 1024
+ * - p {@link CryptoOperationData} Modulus, prime number, 2^(t-1)
+ *
- q {@link CryptoOperationData} Order of cyclic group, prime number, 2^254
+ * - a {@link CryptoOperationData} Generator, integer, 1
+ *
+ *
+ * - GenerateKey mode (SIGN and DH) version = 2001 or 2012
+ *
+ * - namedCurve Paramset for key generation algorithm. If specified no additianal parameters required
+ *
+ * Additional EC parameters, if namedCurve not specified
+ *
+ * - p {@link CryptoOperationData} Prime number - elliptic curve modulus
+ * - a {@link CryptoOperationData} Coefficients a of the elliptic curve E
+ * - b {@link CryptoOperationData} Coefficients b of the elliptic curve E
+ * - q {@link CryptoOperationData} Prime number - order of cyclic group
+ * - x {@link CryptoOperationData} Base point p x-coordinate
+ * - y {@link CryptoOperationData} Base point p y-coordinate
+ *
+ *
+ *
+ *
+ * @class GostSign
+ * @param {AlgorithmIndentifier} algorithm
+ */
+function GostSign(algorithm) //
+{
+ algorithm = algorithm || {};
+ this.name = (algorithm.name || 'GOST R 34.10') + '-' +
+ ((algorithm.version || 2012) % 100) + '-' + (algorithm.length || 256) +
+ (((algorithm.mode || 'SIGN') !== 'SIGN') ? '-' + algorithm.mode : '') +
+ (typeof algorithm.namedParam === 'string' ? '/' + algorithm.namedParam : '') +
+ (typeof algorithm.namedCurve === 'string' ? '/' + algorithm.namedCurve : '') +
+ (typeof algorithm.sBox === 'string' ? '/' + algorithm.sBox : '');
+
+ var version = algorithm.version || 2012;
+
+ // Functions
+ switch (algorithm.mode || 'SIGN') {
+ case 'SIGN':
+ this.sign = sign;
+ this.verify = verify;
+ this.generateKey = generateKey;
+ break;
+ case 'DH':
+ this.deriveBits = deriveBits;
+ this.deriveKey = deriveKey;
+ this.generateKey = generateKey;
+ break;
+ case 'MASK':
+ this.wrapKey = wrapKey;
+ this.unwrapKey = unwrapKey;
+ this.generateKey = generateMaskKey;
+ break;
+ }
+
+ // Define parameters
+ if (version === 1994) {
+ // Named or parameters algorithm
+ var param = algorithm.param;
+ if (!param)
+ param = GostParams[this.namedParam = (algorithm.namedParam || 'S-A').toUpperCase()];
+ this.modulusLength = algorithm.modulusLength || param.modulusLength || 1024;
+ this.p = htobi(param.p);
+ this.q = htobi(param.q);
+ this.a = htobi(param.a);
+ // Public key for derive
+ if (algorithm['public'])
+ this.peer_y = atobi(algorithm['public']);
+ } else {
+ // Named or parameters algorithm
+ var param = algorithm.curve;
+ if (!param)
+ param = ECGostParams[this.namedCurve = (algorithm.namedCurve || 'S-256-A').toUpperCase()];
+ var curve = this.curve = newCurve(htobi(param.p), htobi(param.a), htobi(param.b));
+ this.P = newEC(curve,
+ newFE(curve, htobi(param.x)),
+ newFE(curve, htobi(param.y)));
+ this.q = htobi(param.q);
+ // Public key for derive
+ if (algorithm['public']) {
+ var k2 = to2(algorithm['public']);
+ this.peer_Q = new newEC(this.curve, // This corresponds to the binary representation of (256||256)
+ newFE(this.curve, k2[0]), // first 32 octets contain the little-endian representation of x
+ newFE(this.curve, k2[1])); // and second 32 octets contain the little-endian representation of y.
+ }
+ }
+
+ // Check bit length
+ var hashLen, keyLen;
+ if (this.curve) {
+ keyLen = algorithm.length || bitLength(this.q);
+ if (keyLen > 508 && keyLen <= 512)
+ keyLen = 512;
+ else if (keyLen > 254 && keyLen <= 256)
+ keyLen = 256;
+ else
+ throw new NotSupportedError('Support keys only 256 or 512 bits length');
+ hashLen = keyLen;
+ } else {
+ keyLen = algorithm.modulusLength || bitLength(this.p);
+ if (keyLen > 1016 && keyLen <= 1024)
+ keyLen = 1024;
+ else if (keyLen > 508 && keyLen <= 512)
+ keyLen = 512;
+ else
+ throw new NotSupportedError('Support keys only 512 or 1024 bits length');
+ hashLen = 256;
+ }
+ this.bitLength = hashLen;
+ this.keyLength = keyLen;
+
+ // Algorithm proceator for result conversion
+ this.procreator = algorithm.procreator;
+
+ // Hash function definition
+ var hash = algorithm.hash;
+ if (hash) {
+ if (typeof hash === 'string' || hash instanceof String)
+ hash = {name: hash};
+ if (algorithm.version === 1994 || algorithm.version === 2001) {
+ hash.version = 1994;
+ hash.length = 256;
+ hash.sBox = algorithm.sBox || hash.sBox;
+ } else {
+ hash.version = 2012;
+ hash.length = hashLen;
+ }
+ hash.procreator = hash.procreator || algorithm.procreator;
+
+ if (!GostDigest)
+ GostDigest = root.GostDigest;
+ if (!GostDigest)
+ throw new NotSupportedError('Object GostDigest not found');
+
+ this.hash = new GostDigest(hash);
+ }
+
+ // Pregenerated seed for key exchange algorithms
+ if (algorithm.ukm) // Now don't check size
+ this.ukm = algorithm.ukm;
+
+} //
+
+export default GostSign;
diff --git a/tests/operations/tests/Hash.mjs b/tests/operations/tests/Hash.mjs
index 31ebc3a6..430ad05a 100644
--- a/tests/operations/tests/Hash.mjs
+++ b/tests/operations/tests/Hash.mjs
@@ -1033,6 +1033,84 @@ TestRegister.addTests([
}
]
},
+ {
+ name: "Streebog-256: Test Case 1",
+ input: "",
+ expectedOutput: "3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb",
+ recipeConfig: [
+ {
+ op: "Streebog",
+ args: [
+ "2012", "D-A", "256"
+ ]
+ }
+ ]
+ },
+ {
+ name: "Streebog-256: Test Case 2",
+ input: "The quick brown fox jumps over the lazy dog",
+ expectedOutput: "3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4",
+ recipeConfig: [
+ {
+ op: "Streebog",
+ args: [
+ "2012", "D-A", "256"
+ ]
+ }
+ ]
+ },
+ {
+ name: "Streebog-512: Test Case 1",
+ input: "",
+ expectedOutput: "8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a",
+ recipeConfig: [
+ {
+ op: "Streebog",
+ args: [
+ "2012", "D-A", "512"
+ ]
+ }
+ ]
+ },
+ {
+ name: "Streebog-512: Test Case 2",
+ input: "The quick brown fox jumps over the lazy dog",
+ expectedOutput: "d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe",
+ recipeConfig: [
+ {
+ op: "Streebog",
+ args: [
+ "2012", "D-A", "512"
+ ]
+ }
+ ]
+ },
+ {
+ name: "GOST R 34.11-94: Test Case 1",
+ input: "",
+ expectedOutput: "981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0",
+ recipeConfig: [
+ {
+ op: "Streebog",
+ args: [
+ "1994", "D-A", "256"
+ ]
+ }
+ ]
+ },
+ {
+ name: "GOST R 34.11-94: Test Case 2",
+ input: "This is message, length=32 bytes",
+ expectedOutput: "2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb",
+ recipeConfig: [
+ {
+ op: "Streebog",
+ args: [
+ "1994", "D-A", "256"
+ ]
+ }
+ ]
+ }
/*{ // This takes a LONG time to run (over a minute usually).
name: "Scrypt: RFC test vector 4",
input: "pleaseletmein",