WebCrypto GOST Source: gostEngine.js

/**
 * @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.
 * 
 */

(function (root, factory) {

    /*
     * Module imports and exports
     * 
     */ // <editor-fold defaultstate="collapsed">
    if (typeof define === 'function' && define.amd) {
        define(['gostRandom', 'gostCipher', 'gostDigest', 'gostSign'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('gostRandom'), require('gostCipher'), require('gostDigest'), require('gostSign'));
    } else {
        if (typeof importScripts !== 'undefined') {
            if (!(root.GostRandom && root.GostCipher && root.GostDigest && root.GostSign))
                importScripts('gostRandom.js', 'gostCipher.js', 'gostDigest.js', 'gostSign.js');
        }
        root.gostEngine = factory(root.GostRandom, root.GostCipher, root.GostDigest, root.GostSign);
    }
    // </editor-fold>

}(this, function (GostRandom, GostCipher, GostDigest, GostSign) {

    /*
     * Engine definition base on normalized algorithm identifier
     * 
     */ // <editor-fold defaultstate="collapsed">

    var root = this;

    // 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);
    } // </editor-fold>

    /**
     * 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:
     *  <ul>
     *      <li>GostCipher - implementation of GOST 28147, GOST R 34.12, GOST R 34.13 Encryption algorithms. Reference {@link http://tools.ietf.org/html/rfc5830}</li>
     *      <li>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}</li>
     *      <li>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}</li>
     *  </ul>
     * @namespace gostEngine
     */
    var gostEngine = {
        /**
         * gostEngine.execute(algorithm, method, args) Entry point to execution 
         * all low-level GOST cryptographic methods
         * 
         *  <ul>
         *      <li>Determine the appropriate engine for a given execution method</li>
         *      <li>Create cipher object for determineted engine</li>
         *      <li>Execute method of cipher with given args</li>
         *  </ul>
         * 
         * @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) // <editor-fold defaultstate="collapsed">
        {
            // 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);
        }, // </editor-fold>
        /**
         * gostEngine.getGostCipher(algorithm) returns GOST 28147 / GOST R 34.12 cipher instance<br><br>
         * 
         * GOST 28147-89 / GOST R 34.12-15 Encryption Algorithm<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>
         *              <li><b>MASK</b> Key mask 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>
         *      <li>Wrap/Unwrap key mode (MASK)</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" (64-bit CFB) 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 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>
         *          </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>
         * 
         * @memberOf gostEngine
         * @param {AlgorithmIndentifier} algorithm Algorithm identifier
         * @returns {GostCipher} Instance of GostCipher
         */
        getGostCipher: function (algorithm) // <editor-fold defaultstate="collapsed">
        {
            return new (GostCipher || (GostCipher = root.GostCipher))(algorithm);
        }, // </editor-fold>
        /**
         * gostEngine.getGostDigest(algorithm) returns GOST R 34.11 cipher instance<br><br>
         * 
         * Normalized algorithm identifier common parameters:
         * 
         *  <ul>
         *      <li><b>name</b> Algorithm name 'GOST R 34.11'</li>
         *      <li><b>version</b> Algorithm version
         *          <ul>
         *              <li><b>1994</b> old-style 256 bits digest based on GOST 28147-89</li>
         *              <li><b>2012</b> 256 ro 512 bits digest algorithm "Streebog" GOST R 34.11-2012 (default)</li>
         *          </ul>
         *      </li>
         *      <li><b>length</b> Digest length
         *          <ul>
         *              <li><b>256</b> 256 bits digest</li>
         *              <li><b>512</b> 512 bits digest, valid only for algorithm "Streebog"</li>
         *          </ul>
         *      </li>
         *      <li><b>mode</b> Algorithm mode
         *          <ul>
         *              <li><b>HASH</b> simple digest mode (default)</li>
         *              <li><b>HMAC</b> HMAC algorithm based on GOST R 34.11</li>
         *              <li><b>KDF</b> Derive bits for KEK deversification</li>
         *              <li><b>PBKDF2</b> Password based key dirivation algorithms PBKDF2 (based on HMAC)</li>
         *              <li><b>PFXKDF</b> PFX key dirivation algorithms PFXKDF</li>
         *              <li><b>CPKDF</b> CryptoPro Password based key dirivation algorithms</li>
         *          </ul>
         *      </li>
         *      <li><b>sBox</b> Paramset sBox for GOST 28147-89. Used only if version = 1994</li>
         *  </ul>
         * 
         * Supported algorithms, modes and parameters:
         * 
         *  <ul>
         *      <li>Digest HASH mode (default)</li>
         *      <li>Sign/Verify HMAC modes parameters depends on version and length
         *          <ul>
         *              <li><b>version: 1994</b> HMAC parameters (B = 32, L = 32)</li>
         *              <li><b>version: 2012, length: 256</b> HMAC parameters (B = 64, L = 32)</li>
         *              <li><b>version: 2012, length: 512</b> HMAC parameters  (B = 64, L = 64)</li>
         *          </ul>
         *      </li>
         *      <li>DeriveBits/DeriveKey KDF mode
         *          <ul>
         *              <li><b>context</b> {@link CryptoOperationData} Context of the key derivation</li>
         *              <li><b>label</b> {@link CryptoOperationData} Label that identifies the purpose for the derived keying material</li>
         *          </ul>
         *      </li>
         *      <li>DeriveBits/DeriveKey PBKDF2 mode
         *          <ul>
         *              <li><b>salt</b> {@link CryptoOperationData} Random salt as input for HMAC algorithm</li>
         *              <li><b>iterations</b> Iteration count. GOST recomended value 1000 (default) or 2000</li>
         *          </ul>
         *      </li>
         *      <li>DeriveBits/DeriveKey PFXKDF mode
         *          <ul>
         *              <li><b>salt</b> {@link CryptoOperationData} Random salt as input for HMAC algorithm</li>
         *              <li><b>iterations</b> Iteration count. GOST recomended value 1000 (default) or 2000</li>
         *              <li><b>diversifier</b> Deversifier, ID=1 - key material for performing encryption or decryption, 
         *              ID=2 - IV (Initial Value) for encryption or decryption, ID=3 - integrity key for MACing</li>
         *          </ul>
         *      </li>
         *      <li>DeriveBits/DeriveKey CPKDF mode
         *          <ul>
         *              <li><b>salt</b> {@link CryptoOperationData} Random salt as input for HMAC algorithm</li>
         *              <li><b>iterations</b> Iteration count. GOST recomended value 1000 (default) or 2000</li>
         *          </ul>
         *      </li>
         *  </ul>
         * 
         * @memberOf gostEngine
         * @param {AlgorithmIndentifier} algorithm Algorithm identifier
         * @returns {GostDigest} Instance of GostDigest
         */
        getGostDigest: function (algorithm) // <editor-fold defaultstate="collapsed">
        {
            return new (GostDigest || (GostDigest = root.GostDigest))(algorithm);
        }, // </editor-fold>
        /**
         * gostEngine.getGostSign(algorithm) returns GOST R 34.10 cipher instance<br><br>
         * 
         * Normalized algorithm identifier common parameters:
         * 
         *  <ul>
         *      <li><b>name</b> Algorithm name 'GOST R 34.10'</li>
         *      <li><b>version</b> Algorithm version
         *          <ul>
         *              <li><b>1994</b> - Old-style GOST R 34.10-94 ExpMod algorithm with GOST R 34.11-94 hash</li>
         *              <li><b>2001</b> - GOST R 34.10-2001 Eliptic curve algorithm with old GOST R 34.11-94 hash</li>
         *              <li><b>2012</b> - GOST R 34.10-2012 Eliptic curve algorithm with GOST R 34.11-12 hash, default mode</li>
         *          </ul>
         *      </li>
         *      <li><b>length</b> Length of hash and signature. Key length == hash length for EC algorithms and 2 * hash length for ExpMod algorithm
         *          <ul>
         *              <li><b>GOST R 34.10-256</b> - 256 bits digest, default mode</li>
         *              <li><b>GOST R 34.10-512</b> - 512 bits digest only for GOST R 34.11-2012 hash</li>
         *          </ul>
         *      </li>
         *      <li><b>mode</b> Algorithm mode
         *          <ul>
         *              <li><b>SIGN</b> Digital signature mode (default)</li>
         *              <li><b>DH</b> Diffie-Hellman key generation and key agreement mode</li>
         *              <li><b>MASK</b> Key mask mode</li>
         *          </ul>
         *      </li>
         *      <li><b>sBox</b> Paramset sBox for GOST 34.11-94. Used only if version = 1994 or 2001</li>
         *  </ul>
         *  
         * Supported algorithms, modes and parameters:
         * 
         *  <ul>
         *      <li>Sign/Verify mode (SIGN)</li>
         *      <li>Wrap/Unwrap mode (MASK)</li>
         *      <li>DeriveKey/DeriveBits mode (DH)
         *          <ul>
         *              <li>{@link CryptoOperationData} <b>ukm</b> User key material. Default - random generated value</li>
         *              <li>{@link CryptoOperationData} <b>public</b> The peer's EC public key data</li>
         *          </ul>
         *      </li>
         *      <li>GenerateKey mode (SIGN and DH and MASK) version = 1994
         *          <ul>
         *              <li><b>namedParam</b> Paramset for key generation algorithm. If specified no additianal parameters required</li>
         *          </ul>
         *          Additional parameters, if namedParam not specified
         *          <ul>
         *              <li><b>modulusLength</b> Bit length of p (512 or 1024 bits). Default = 1024</li>
         *              <li><b>p</b> {@link CryptoOperationData} Modulus, prime number, 2^(t-1)<p<2^t</li>
         *              <li><b>q</b> {@link CryptoOperationData} Order of cyclic group, prime number, 2^254<q<2^256, q is a factor of p-1</li>
         *              <li><b>a</b> {@link CryptoOperationData} Generator, integer, 1<a<p-1, at that aq (mod p) = 1</li>
         *          </ul>
         *      </li>
         *      <li>GenerateKey mode (SIGN and DH and MASK) version = 2001 or 2012
         *          <ul>
         *              <li><b>namedCurve</b> Paramset for key generation algorithm. If specified no additianal parameters required</li>
         *          </ul>
         *          Additional EC parameters, if namedCurve not specified
         *          <ul>
         *              <li><b>p</b> {@link CryptoOperationData} Prime number - elliptic curve modulus</li>
         *              <li><b>a</b> {@link CryptoOperationData} Coefficients a of the elliptic curve E</li>
         *              <li><b>b</b> {@link CryptoOperationData} Coefficients b of the elliptic curve E</li>
         *              <li><b>q</b> {@link CryptoOperationData} Prime number - order of cyclic group</li>
         *              <li><b>x</b> {@link CryptoOperationData} Base point p x-coordinate</li>
         *              <li><b>y</b> {@link CryptoOperationData} Base point p y-coordinate</li>
         *          </ul>
         *      </li>
         *  </ul>
         *  
         * @memberOf gostEngine
         * @param {AlgorithmIndentifier} algorithm Algorithm identifier
         * @returns {GostSign} Instance of GostSign
         */
        getGostSign: function (algorithm) // <editor-fold defaultstate="collapsed">
        {
            return new (GostSign || (GostSign = root.GostSign))(algorithm);
        } // </editor-fold>
    };

    /*
     * Worker method execution
     * 
     */ // <editor-fold defaultstate="collapsed">

    // 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.<br>
         * Call method execute and postMessage() results to onmessage event handler 
         * in the main process.<br>
         * 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');
    } // </editor-fold>

    return gostEngine;

}));