|
@@ -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
|
|
|
+*
|
|
|
+*/ // <editor-fold defaultstate="collapsed">
|
|
|
+
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+// </editor-fold>
|
|
|
+
|
|
|
+/*
|
|
|
+ * Initial parameters and common algortithms of GOST R 34.12-15
|
|
|
+ * Algorithm "Kuznechik" 128bit
|
|
|
+ *
|
|
|
+ */ // <editor-fold defaultstate="collapsed">
|
|
|
+
|
|
|
+// 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);
|
|
|
+}
|
|
|
+
|
|
|
+// </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Key schedule for GOST R 34.12-15 128bits
|
|
|
+ *
|
|
|
+ * @memberOf GostCipher
|
|
|
+ * @private
|
|
|
+ * @instance
|
|
|
+ * @method keySchedule
|
|
|
+ * @param {type} k
|
|
|
+ * @returns {Uint8Array}
|
|
|
+ */
|
|
|
+function keySchedule128(k) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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));
|
|
|
+ }
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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]);
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Key schedule for RC2
|
|
|
+ *
|
|
|
+ * https://tools.ietf.org/html/rfc2268
|
|
|
+ *
|
|
|
+ * @memberOf GostCipher
|
|
|
+ * @private
|
|
|
+ * @instance
|
|
|
+ * @method keySchedule
|
|
|
+ * @param {Uint8Array} k
|
|
|
+ * @returns {Uint16Array}
|
|
|
+ */
|
|
|
+var keyScheduleRC2 = (function () // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ // 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;
|
|
|
+ };
|
|
|
+} // </editor-fold>
|
|
|
+)();
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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 () // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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);
|
|
|
+ };
|
|
|
+} // </editor-fold>
|
|
|
+)();
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-ECB<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-ECB<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CFB<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CFB<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-OFB<br><br>
|
|
|
+ *
|
|
|
+ * 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<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CTR<br><br>
|
|
|
+ *
|
|
|
+ * 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<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+function processCTR15(k, d, iv) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CBC<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CBC<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * The generateKey method returns a new generated key.
|
|
|
+ *
|
|
|
+ * @memberOf GostCipher
|
|
|
+ * @method generateKey
|
|
|
+ * @instance
|
|
|
+ * @return {CryptoOperationData} result
|
|
|
+ */
|
|
|
+
|
|
|
+function generateKey() // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ // Simple generate 256 bit random seed
|
|
|
+ var k = new Uint8Array(this.keySize);
|
|
|
+ randomSeed(k);
|
|
|
+ return k.buffer;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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]);
|
|
|
+ }
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+function processKeyMAC15(s) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+function processMAC15(key, s, d) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-KW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-KW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CPKW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CPKW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CPKW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-SCKW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-SCKW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-SCKW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-SCKW<br><br>
|
|
|
+ *
|
|
|
+ * SignalCom master key generation for wrapping
|
|
|
+ *
|
|
|
+ * @memberOf GostCipher
|
|
|
+ * @method generateKey
|
|
|
+ * @instance
|
|
|
+ * @return {CryptoOperationData} result
|
|
|
+ */
|
|
|
+function generateWrappingKeySC() // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ return packKeySC.call(this, generateKey.call(this));
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+function maskKey(mask, key, inverse, keySize) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-MASK<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ return maskKey(mask, key, this.procreator === 'VN', this.keySize);
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CPKW<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ return maskKey(mask, key, this.procreator !== 'VN', this.keySize);
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-CPKM<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ return k;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-NoPadding<br><br>
|
|
|
+ *
|
|
|
+ * No padding.
|
|
|
+ *
|
|
|
+ * @memberOf GostCipher
|
|
|
+ * @method padding
|
|
|
+ * @instance
|
|
|
+ * @private
|
|
|
+ * @param {Uint8Array} d array with source data
|
|
|
+ * @returns {Uint8Array} result
|
|
|
+ */
|
|
|
+function noPad(d) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ return new Uint8Array(d);
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-PKCS5Padding<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+function pkcs5Unpad(d) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-ZeroPadding<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-BitPadding<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+function bitUnpad(d) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * Algorithm name GOST 28147-RandomPadding<br><br>
|
|
|
+ *
|
|
|
+ * 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) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ 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;
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+/**
|
|
|
+ * GOST 28147-89 Encryption Algorithm<br><br>
|
|
|
+ *
|
|
|
+ * References {@link http://tools.ietf.org/html/rfc5830}<br><br>
|
|
|
+ *
|
|
|
+ * When keys and initialization vectors are converted to/from byte arrays,
|
|
|
+ * little-endian byte order is assumed.<br><br>
|
|
|
+ *
|
|
|
+ * Normalized algorithm identifier common parameters:
|
|
|
+ *
|
|
|
+ * <ul>
|
|
|
+ * <li><b>name</b> Algorithm name 'GOST 28147' or 'GOST R 34.12'</li>
|
|
|
+ * <li><b>version</b> Algorithm version, number
|
|
|
+ * <ul>
|
|
|
+ * <li><b>1989</b> Current version of standard</li>
|
|
|
+ * <li><b>2015</b> New draft version of standard</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li><b>length</b> Block length
|
|
|
+ * <ul>
|
|
|
+ * <li><b>64</b> 64 bits length (default)</li>
|
|
|
+ * <li><b>128</b> 128 bits length (only for version 2015)</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li><b>mode</b> Algorithm mode, string
|
|
|
+ * <ul>
|
|
|
+ * <li><b>ES</b> Encryption mode (default)</li>
|
|
|
+ * <li><b>MAC</b> "imitovstavka" (MAC) mode</li>
|
|
|
+ * <li><b>KW</b> Key wrapping mode</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li><b>sBox</b> Paramset sBox for GOST 28147-89, string. Used only if version = 1989</li>
|
|
|
+ * </ul>
|
|
|
+ *
|
|
|
+ * Supported algorithms, modes and parameters:
|
|
|
+ *
|
|
|
+ * <ul>
|
|
|
+ * <li>Encript/Decrypt mode (ES)
|
|
|
+ * <ul>
|
|
|
+ * <li><b>block</b> Block mode, string. Default ECB</li>
|
|
|
+ * <li><b>keyMeshing</b> Key meshing mode, string. Default NO</li>
|
|
|
+ * <li><b>padding</b> Padding mode, string. Default NO for CFB and CTR modes, or ZERO for others</li>
|
|
|
+ * <li><b>iv</b> {@link CryptoOperationData} Initial vector with length of block. Default - zero block</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li>Sign/Verify mode (MAC)
|
|
|
+ * <ul>
|
|
|
+ * <li><b>macLength</b> Length of mac in bits (default - 32 bits)</li>
|
|
|
+ * <li><b>iv</b> {@link CryptoOperationData} Initial vector with length of block. Default - zero block</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li>Wrap/Unwrap key mode (KW)
|
|
|
+ * <ul>
|
|
|
+ * <li><b>keyWrapping</b> Mode of keywrapping, string. Default NO - standard GOST key wrapping</li>
|
|
|
+ * <li><b>ukm</b> {@link CryptoOperationData} User key material. Default - random generated value</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * </ul>
|
|
|
+ *
|
|
|
+ * Supported paramters values:
|
|
|
+ *
|
|
|
+ * <ul>
|
|
|
+ * <li>Block modes (parameter 'block')
|
|
|
+ * <ul>
|
|
|
+ * <li><b>ECB</b> "prostaya zamena" (ECB) mode (default)</li>
|
|
|
+ * <li><b>CFB</b> "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode</li>
|
|
|
+ * <li><b>OFB</b> "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode</li>
|
|
|
+ * <li><b>CTR</b> "gammirovanie" (counter) mode</li>
|
|
|
+ * <li><b>CBC</b> Cipher-Block-Chaining (CBC) mode</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li>Key meshing modes (parameter 'keyMeshing')
|
|
|
+ * <ul>
|
|
|
+ * <li><b>NO</b> No key wrapping (default)</li>
|
|
|
+ * <li><b>CP</b> CryptoPor Key key meshing</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li>Padding modes (parameter 'padding')
|
|
|
+ * <ul>
|
|
|
+ * <li><b>NO</b> No padding only for CFB, OFB and CTR modes</li>
|
|
|
+ * <li><b>PKCS5</b> PKCS#5 padding mode</li>
|
|
|
+ * <li><b>ZERO</b> Zero bits padding mode</li>
|
|
|
+ * <li><b>RANDOM</b> Random bits padding mode</li>
|
|
|
+ * <li><b>BIT</b> One bit padding mode</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * <li>Wrapping key modes (parameter 'keyWrapping')
|
|
|
+ * <ul>
|
|
|
+ * <li><b>NO</b> Ref. rfc4357 6.1 GOST 28147-89 Key wrapping</li>
|
|
|
+ * <li><b>CP</b> CryptoPro Key wrapping mode</li>
|
|
|
+ * <li><b>SC</b> SignalCom Key wrapping mode</li>
|
|
|
+ * </ul>
|
|
|
+ * </li>
|
|
|
+ * </ul>
|
|
|
+ *
|
|
|
+ * @class GostCipher
|
|
|
+ * @param {AlgorithmIndentifier} algorithm WebCryptoAPI algorithm identifier
|
|
|
+ */
|
|
|
+function GostCipher(algorithm) // <editor-fold defaultstate="collapsed">
|
|
|
+{
|
|
|
+ // 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');
|
|
|
+ }
|
|
|
+} // </editor-fold>
|
|
|
+
|
|
|
+export default GostCipher;
|