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

1654 lines
68 KiB
JavaScript

/**
* Implementation Web Crypto interfaces for GOST algorithms
* 1.76
* 2014-2016, Rudolf Nickolaev. All rights reserved.
*
* Exported for CyberChef by mshwed [m@ttshwed.com]
*/
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
import GostRandom from './gostRandom.mjs';
import gostEngine from './gostEngine.mjs';
import crypto from 'crypto'
/*
* Algorithm normalization
*
*/ // <editor-fold defaultstate="collapsed">
var root = {};
root.gostEngine = gostEngine;
var rootCrypto = crypto
var SyntaxError = Error,
DataError = Error,
NotSupportedError = Error,
OperationError = Error,
InvalidStateError = Error,
InvalidAccessError = Error;
// Normalize algorithm
function normalize(algorithm, method) {
if (typeof algorithm === 'string' || algorithm instanceof String)
algorithm = {name: algorithm};
var name = algorithm.name;
if (!name)
throw new SyntaxError('Algorithm name not defined');
// Extract algorithm modes from name
var modes = name.split('/'), modes = modes[0].split('-').concat(modes.slice(1));
// Normalize the name with default modes
var na = {};
name = modes[0].replace(/[\.\s]/g, '');
modes = modes.slice(1);
if (name.indexOf('28147') >= 0) {
na = {
name: 'GOST 28147',
version: 1989,
mode: (algorithm.mode || (// ES, MAC, KW
(method === 'sign' || method === 'verify') ? 'MAC' :
(method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(),
length: algorithm.length || 64
};
} else if (name.indexOf('3412') >= 0) {
na = {
name: 'GOST R 34.12',
version: 2015,
mode: (algorithm.mode || (// ES, MAC, KW
(method === 'sign' || method === 'verify') ? 'MAC' :
(method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(),
length: algorithm.length || 64 // 128
};
} else if (name.indexOf('3411') >= 0) {
na = {
name: 'GOST R 34.11',
version: 2012, // 1994
mode: (algorithm.mode || (// HASH, KDF, HMAC, PBKDF2, PFXKDF, CPKDF
(method === 'deriveKey' || method === 'deriveBits') ? 'KDF' :
(method === 'sign' || method === 'verify') ? 'HMAC' : 'HASH')).toUpperCase(),
length: algorithm.length || 256 // 512
};
} else if (name.indexOf('3410') >= 0) {
na = {
name: 'GOST R 34.10',
version: 2012, // 1994, 2001
mode: (algorithm.mode || (// SIGN, DH, MASK
(method === 'deriveKey' || method === 'deriveBits') ? 'DH' : 'SIGN')).toUpperCase(),
length: algorithm.length || 256 // 512
};
} else if (name.indexOf('SHA') >= 0) {
na = {
name: 'SHA',
version: (algorithm.length || 160) === 160 ? 1 : 2, // 1, 2
mode: (algorithm.mode || (// HASH, KDF, HMAC, PBKDF2, PFXKDF
(method === 'deriveKey' || method === 'deriveBits') ? 'KDF' :
(method === 'sign' || method === 'verify') ? 'HMAC' : 'HASH')).toUpperCase(),
length: algorithm.length || 160
};
} else if (name.indexOf('RC2') >= 0) {
na = {
name: 'RC2',
version: 1,
mode: (algorithm.mode || (// ES, MAC, KW
(method === 'sign' || method === 'verify') ? 'MAC' :
(method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(),
length: algorithm.length || 32 // 1 - 1024
};
} else if (name.indexOf('PBKDF2') >= 0) {
na = normalize(algorithm.hash, 'digest');
na.mode = 'PBKDF2';
} else if (name.indexOf('PFXKDF') >= 0) {
na = normalize(algorithm.hash, 'digest');
na.mode = 'PFXKDF';
} else if (name.indexOf('CPKDF') >= 0) {
na = normalize(algorithm.hash, 'digest');
na.mode = 'CPKDF';
} else if (name.indexOf('HMAC') >= 0) {
na = normalize(algorithm.hash, 'digest');
na.mode = 'HMAC';
} else
throw new NotSupportedError('Algorithm not supported');
// Compile modes
modes.forEach(function (mode) {
mode = mode.toUpperCase();
if (/^[0-9]+$/.test(mode)) {
if ((['8', '16', '32'].indexOf(mode) >= 0) || (na.length === '128' && mode === '64')) { // Shift bits
if (na.mode === 'ES')
na.shiftBits = parseInt(mode);
else if (na.mode === 'MAC')
na.macLength = parseInt(mode);
else
throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported');
} else if (['89', '94', '01', '12', '15', '1989', '1994', '2001', '2012', '2015'].indexOf(mode) >= 0) { // GOST Year
var version = parseInt(mode);
version = version < 1900 ? (version < 80 ? 2000 + version : 1900 + version) : version;
na.version = version;
} else if (['1'].indexOf(mode) >= 0 && na.name === 'SHA') { // SHA-1
na.version = 1;
na.length = 160;
} else if (['256', '384', '512'].indexOf(mode) >= 0 && na.name === 'SHA') { // SHA-2
na.version = 2;
na.length = parseInt(mode);
} else if (['40', '128'].indexOf(mode) >= 0 && na.name === 'RC2') { // RC2
na.version = 1;
na.length = parseInt(mode); // key size
} else if (['64', '128', '256', '512'].indexOf(mode) >= 0) // block size
na.length = parseInt(mode);
else if (['1000', '2000'].indexOf(mode) >= 0) // Iterations
na.iterations = parseInt(mode);
// Named Paramsets
} else if (['E-TEST', 'E-A', 'E-B', 'E-C', 'E-D', 'E-SC', 'E-Z', 'D-TEST', 'D-A', 'D-SC'].indexOf(mode) >= 0) {
na.sBox = mode;
} else if (['S-TEST', 'S-A', 'S-B', 'S-C', 'S-D', 'X-A', 'X-B', 'X-C'].indexOf(mode) >= 0) {
na.namedParam = mode;
} else if (['S-256-TEST', 'S-256-A', 'S-256-B', 'S-256-C', 'P-256', 'T-512-TEST', 'T-512-A',
'T-512-B', 'X-256-A', 'X-256-B', 'T-256-TEST', 'T-256-A', 'T-256-B', 'S-256-B', 'T-256-C', 'S-256-C'].indexOf(mode) >= 0) {
na.namedCurve = mode;
} else if (['SC', 'CP', 'VN'].indexOf(mode) >= 0) {
na.procreator = mode;
// Encription GOST 28147 or GOST R 34.12
} else if (na.name === 'GOST 28147' || na.name === 'GOST R 34.12' || na.name === 'RC2') {
if (['ES', 'MAC', 'KW', 'MASK'].indexOf(mode) >= 0) {
na.mode = mode;
} else if (['ECB', 'CFB', 'OFB', 'CTR', 'CBC'].indexOf(mode) >= 0) {
na.mode = 'ES';
na.block = mode;
} else if (['CPKW', 'NOKW', 'SCKW'].indexOf(mode) >= 0) {
na.mode = 'KW';
na.keyWrapping = mode.replace('KW', '');
} else if (['ZEROPADDING', 'PKCS5PADDING', 'NOPADDING', 'RANDOMPADDING', 'BITPADDING'].indexOf(mode) >= 0) {
na.padding = mode.replace('PADDING', '');
} else if (['NOKM', 'CPKM'].indexOf(mode) >= 0) {
na.keyMeshing = mode.replace('KM', '');
} else
throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported');
// Digesting GOST 34.11
} else if (na.name === 'GOST R 34.11' || na.name === 'SHA') {
if (['HASH', 'KDF', 'HMAC', 'PBKDF2', 'PFXKDF', 'CPKDF'].indexOf(mode) >= 0)
na.mode = mode;
else
throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported');
// Signing GOST 34.10
} else if (na.name === 'GOST R 34.10') {
var hash = mode.replace(/[\.\s]/g, '');
if (hash.indexOf('GOST') >= 0 && hash.indexOf('3411') >= 0)
na.hash = mode;
else if (['SIGN', 'DH', 'MASK'].indexOf(mode))
na.mode = mode;
else
throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported');
}
});
// Procreator
na.procreator = algorithm.procreator || na.procreator || 'CP';
// Key size
switch (na.name) {
case 'GOST R 34.10':
na.keySize = na.length / (na.version === 1994 ? 4 : 8);
break;
case 'GOST R 34.11':
na.keySize = 32;
break;
case 'GOST 28147':
case 'GOST R 34.12':
na.keySize = 32;
break;
case 'RC2':
na.keySize = Math.ceil(na.length / 8);
break;
case 'SHA':
na.keySize = na.length / 8;
break;
}
// Encrypt additional modes
if (na.mode === 'ES') {
if (algorithm.block)
na.block = algorithm.block; // ECB, CFB, OFB, CTR, CBC
if (na.block)
na.block = na.block.toUpperCase();
if (algorithm.padding)
na.padding = algorithm.padding; // NO, ZERO, PKCS5, RANDOM, BIT
if (na.padding)
na.padding = na.padding.toUpperCase();
if (algorithm.shiftBits)
na.shiftBits = algorithm.shiftBits; // 8, 16, 32, 64
if (algorithm.keyMeshing)
na.keyMeshing = algorithm.keyMeshing; // NO, CP
if (na.keyMeshing)
na.keyMeshing = na.keyMeshing.toUpperCase();
// Default values
if (method !== 'importKey' && method !== 'generateKey') {
na.block = na.block || 'ECB';
na.padding = na.padding || (na.block === 'CBC' || na.block === 'ECB' ? 'ZERO' : 'NO');
if (na.block === 'CFB' || na.block === 'OFB')
na.shiftBits = na.shiftBits || na.length;
na.keyMeshing = na.keyMeshing || 'NO';
}
}
if (na.mode === 'KW') {
if (algorithm.keyWrapping)
na.keyWrapping = algorithm.keyWrapping; // NO, CP, SC
if (na.keyWrapping)
na.keyWrapping = na.keyWrapping.toUpperCase();
if (method !== 'importKey' && method !== 'generateKey')
na.keyWrapping = na.keyWrapping || 'NO';
}
// Paramsets
['sBox', 'namedParam', 'namedCurve', 'curve', 'param', 'modulusLength'].forEach(function (name) {
algorithm[name] && (na[name] = algorithm[name]);
});
// Default values
if (method !== 'importKey' && method !== 'generateKey') {
if (na.name === 'GOST 28147') {
na.sBox = na.sBox || (na.procreator === 'SC' ? 'E-SC' : 'E-A'); // 'E-A', 'E-B', 'E-C', 'E-D', 'E-SC'
} else if (na.name === 'GOST R 34.12' && na.length === 64) {
na.sBox = 'E-Z';
} else if (na.name === 'GOST R 34.11' && na.version === 1994) {
na.sBox = na.sBox || (na.procreator === 'SC' ? 'D-SC' : 'D-A'); // 'D-SC'
} else if (na.name === 'GOST R 34.10' && na.version === 1994) {
na.namedParam = na.namedParam || (na.mode === 'DH' ? 'X-A' : 'S-A'); // 'S-B', 'S-C', 'S-D', 'X-B', 'X-C'
} else if (na.name === 'GOST R 34.10' && na.version === 2001) {
na.namedCurve = na.namedCurve || (na.length === 256 ?
na.procreator === 'SC' ? 'P-256' : (na.mode === 'DH' ? 'X-256-A' : 'S-256-A') : // 'S-256-B', 'S-256-C', 'X-256-B', 'T-256-A', 'T-256-B', 'T-256-C', 'P-256'
na.mode === 'T-512-A'); // 'T-512-B', 'T-512-C'
} else if (na.name === 'GOST R 34.10' && na.version === 2012) {
na.namedCurve = na.namedCurve || (na.length === 256 ?
na.procreator === 'SC' ? 'P-256' : (na.mode === 'DH' ? 'X-256-A' : 'S-256-A') : // 'S-256-B', 'S-256-C', 'X-256-B', 'T-256-A', 'T-256-B', 'T-256-C', 'P-256'
na.mode === 'T-512-A'); // 'T-512-B', 'T-512-C'
}
}
// Vectors
switch (na.mode) {
case 'DH':
algorithm.ukm && (na.ukm = algorithm.ukm);
algorithm['public'] && (na['public'] = algorithm['public']);
break;
case 'SIGN':
case 'KW':
algorithm.ukm && (na.ukm = algorithm.ukm);
break;
case 'ES':
case 'MAC':
algorithm.iv && (na.iv = algorithm.iv);
break;
case 'KDF':
algorithm.label && (na.label = algorithm.label);
algorithm.contex && (na.context = algorithm.contex);
break;
case 'PBKDF2':
algorithm.salt && (na.salt = algorithm.salt);
algorithm.iterations && (na.iterations = algorithm.iterations);
algorithm.diversifier && (na.diversifier = algorithm.diversifier);
break;
case 'PFXKDF':
algorithm.salt && (na.salt = algorithm.salt);
algorithm.iterations && (na.iterations = algorithm.iterations);
algorithm.diversifier && (na.diversifier = algorithm.diversifier);
break;
case 'CPKDF':
algorithm.salt && (na.salt = algorithm.salt);
algorithm.iterations && (na.iterations = algorithm.iterations);
break;
}
// Verification method and modes
if (method && (
((na.mode !== 'ES' && na.mode !== 'SIGN' && na.mode !== 'MAC' &&
na.mode !== 'HMAC' && na.mode !== 'KW' && na.mode !== 'DH'
&& na.mode !== 'MASK') &&
(method === 'generateKey')) ||
((na.mode !== 'ES') &&
(method === 'encrypt' || method === 'decrypt')) ||
((na.mode !== 'SIGN' && na.mode !== 'MAC' && na.mode !== 'HMAC') &&
(method === 'sign' || method === 'verify')) ||
((na.mode !== 'HASH') &&
(method === 'digest')) ||
((na.mode !== 'KW' && na.mode !== 'MASK') &&
(method === 'wrapKey' || method === 'unwrapKey')) ||
((na.mode !== 'DH' && na.mode !== 'PBKDF2' && na.mode !== 'PFXKDF' &&
na.mode !== 'CPKDF' && na.mode !== 'KDF') &&
(method === 'deriveKey' || method === 'deriveBits'))))
throw new NotSupportedError('Algorithm mode ' + na.mode + ' not valid for method ' + method);
// Normalize hash algorithm
algorithm.hash && (na.hash = algorithm.hash);
if (na.hash) {
if ((typeof na.hash === 'string' || na.hash instanceof String)
&& na.procreator)
na.hash = na.hash + '/' + na.procreator;
na.hash = normalize(na.hash, 'digest');
}
// Algorithm object identirifer
algorithm.id && (na.id = algorithm.id);
return na;
}
// Check for possibility use native crypto.subtle
function checkNative(algorithm) {
if (!rootCrypto || !rootCrypto.subtle || !algorithm)
return false;
// Prepare name
var name = (typeof algorithm === 'string' || algorithm instanceof String) ?
name = algorithm : algorithm.name;
if (!name)
return false;
name = name.toUpperCase();
// Digest algorithm for key derivation
if ((name.indexOf('KDF') >= 0 || name.indexOf('HMAC') >= 0) && algorithm.hash)
return checkNative(algorithm.hash);
// True if no supported names
return name.indexOf('GOST') === -1 &&
name.indexOf('SHA-1') === -1 &&
name.indexOf('RC2') === -1 &&
name.indexOf('?DES') === -1;
}
// </editor-fold>
/*
* Key conversion methods
*
*/ // <editor-fold defaultstate="collapsed">
// Check key parameter
function checkKey(key, method) {
if (!key.algorithm)
throw new SyntaxError('Key algorithm not defined');
if (!key.algorithm.name)
throw new SyntaxError('Key algorithm name not defined');
var name = key.algorithm.name,
gostCipher = name === 'GOST 28147' || name === 'GOST R 34.12' || name === 'RC2',
gostDigest = name === 'GOST R 34.11' || name === 'SHA',
gostSign = name === 'GOST R 34.10';
if (!gostCipher && !gostSign && !gostDigest)
throw new NotSupportedError('Key algorithm ' + name + ' is unsupproted');
if (!key.type)
throw new SyntaxError('Key type not defined');
if (((gostCipher || gostDigest) && key.type !== 'secret') ||
(gostSign && !(key.type === 'public' || key.type === 'private')))
throw new DataError('Key type ' + key.type + ' is not valid for algorithm ' + name);
if (!key.usages || !key.usages.indexOf)
throw new SyntaxError('Key usages not defined');
for (var i = 0, n = key.usages.length; i < n; i++) {
var md = key.usages[i];
if (((md === 'encrypt' || md === 'decrypt') && key.type !== 'secret') ||
(md === 'sign' && key.type === 'public') ||
(md === 'verify' && key.type === 'private'))
throw new InvalidStateError('Key type ' + key.type + ' is not valid for ' + md);
}
if (method)
if (key.usages.indexOf(method) === -1)
throw new InvalidAccessError('Key usages is not contain method ' + method);
if (!key.buffer)
throw new SyntaxError('Key buffer is not defined');
var size = key.buffer.byteLength * 8, keySize = 8 * key.algorithm.keySize;
if ((key.type === 'secret' && size !== (keySize || 256) &&
(key.usages.indexOf('encrypt') >= 0 || key.usages.indexOf('decrypt') >= 0)) ||
(key.type === 'private' && !(size === 256 || size === 512)) ||
(key.type === 'public' && !(size === 512 || size === 1024)))
throw new SyntaxError('Key buffer has wrong size ' + size + ' bit');
}
// Extract key and enrich cipher algorithm
function extractKey(method, algorithm, key) {
checkKey(key, method);
if (algorithm) {
var params;
switch (algorithm.mode) {
case 'ES':
params = ['sBox', 'keyMeshing', 'padding', 'block'];
break;
case 'SIGN':
params = ['namedCurve', 'namedParam', 'sBox', 'curve', 'param', 'modulusLength'];
break;
case 'MAC':
params = ['sBox'];
break;
case 'KW':
params = ['keyWrapping', 'ukm'];
break;
case 'DH':
params = ['namedCurve', 'namedParam', 'sBox', 'ukm', 'curve', 'param', 'modulusLength'];
break;
case 'KDF':
params = ['context', 'label'];
break;
case 'PBKDF2':
params = ['sBox', 'iterations', 'salt'];
break;
case 'PFXKDF':
params = ['sBox', 'iterations', 'salt', 'diversifier'];
break;
case 'CPKDF':
params = ['sBox', 'salt'];
break;
}
if (params)
params.forEach(function (name) {
key.algorithm[name] && (algorithm[name] = key.algorithm[name]);
});
}
return key.buffer;
}
// Make key definition
function convertKey(algorithm, extractable, keyUsages, keyData, keyType) {
var key = {
type: keyType || (algorithm.name === 'GOST R 34.10' ? 'private' : 'secret'),
extractable: extractable || 'false',
algorithm: algorithm,
usages: keyUsages || [],
buffer: keyData
};
checkKey(key);
return key;
}
function convertKeyPair(publicAlgorithm, privateAlgorithm, extractable, keyUsages, publicBuffer, privateBuffer) {
if (!keyUsages || !keyUsages.indexOf)
throw new SyntaxError('Key usages not defined');
var publicUsages = keyUsages.filter(function (value) {
return value !== 'sign';
});
var privateUsages = keyUsages.filter(function (value) {
return value !== 'verify';
});
return {
publicKey: convertKey(publicAlgorithm, extractable, publicUsages, publicBuffer, 'public'),
privateKey: convertKey(privateAlgorithm, extractable, privateUsages, privateBuffer, 'private')
};
}
// Swap bytes in buffer
function swapBytes(src) {
if (src instanceof CryptoOperationData)
src = new Uint8Array(src);
var dst = new Uint8Array(src.length);
for (var i = 0, n = src.length; i < n; i++)
dst[n - i - 1] = src[i];
return dst.buffer;
}
// </editor-fold>
/**
* Promise stub object (not fulfill specification, only for internal use)
* Class not defined if Promise class already defined in root context<br><br>
*
* The Promise object is used for deferred and asynchronous computations. A Promise is in one of the three states:
* <ul>
* <li>pending: initial state, not fulfilled or rejected.</li>
* <li>fulfilled: successful operation</li>
* <li>rejected: failed operation.</li>
* </ul>
* Another term describing the state is settled: the Promise is either fulfilled or rejected, but not pending.<br><br>
* @class Promise
* @global
* @param {function} executor Function object with two arguments resolve and reject.
* The first argument fulfills the promise, the second argument rejects it.
* We can call these functions, once our operation is completed.
*/ // <editor-fold defaultstate="collapsed">
if (!Promise) {
root.Promise = (function () {
function mswrap(value) {
if (value && value.oncomplete === null && value.onerror === null) {
return new Promise(function (resolve, reject) {
value.oncomplete = function () {
resolve(value.result);
};
value.onerror = function () {
reject(new OperationError(value.toString()));
};
});
} else
return value;
}
function Promise(executor) {
var state = 'pending', result,
resolveQueue = [], rejectQueue = [];
function call(callback) {
try {
callback();
} catch (e) {
}
}
try {
executor(function (value) {
if (state === 'pending') {
state = 'fulfilled';
result = value;
resolveQueue.forEach(call);
}
}, function (reason) {
if (state === 'pending') {
state = 'rejected';
result = reason;
rejectQueue.forEach(call);
}
});
} catch (error) {
if (state === 'pending') {
state = 'rejected';
result = error;
rejectQueue.forEach(call);
}
}
/**
* The then() method returns a Promise. It takes two arguments, both are
* callback functions for the success and failure cases of the Promise.
*
* @method then
* @memberOf Promise
* @instance
* @param {function} onFulfilled A Function called when the Promise is fulfilled. This function has one argument, the fulfillment value.
* @param {function} onRejected A Function called when the Promise is rejected. This function has one argument, the rejection reason.
* @returns {Promise}
*/
this.then = function (onFulfilled, onRejected) {
return new Promise(function (resolve, reject) {
function asyncOnFulfilled() {
var value;
try {
value = onFulfilled ? onFulfilled(result) : result;
} catch (error) {
reject(error);
return;
}
value = mswrap(value);
if (value && value?.then?.call) {
value.then(resolve, reject);
} else {
resolve(value);
}
}
function asyncOnRejected() {
var reason;
try {
reason = onRejected ? onRejected(result) : result;
} catch (error) {
reject(error);
return;
}
reason = mswrap(reason);
if (reason && reason?.then?.call) {
reason.then(resolve, reject);
} else {
reject(reason);
}
}
if (state === 'fulfilled') {
asyncOnFulfilled();
} else if (state === 'rejected') {
asyncOnRejected();
} else {
resolveQueue.push(asyncOnFulfilled);
rejectQueue.push(asyncOnRejected);
}
});
};
/**
* The catch() method returns a Promise and deals with rejected cases only.
* It behaves the same as calling Promise.prototype.then(undefined, onRejected).
*
* @method catch
* @memberOf Promise
* @instance
* @param {function} onRejected A Function called when the Promise is rejected. This function has one argument, the rejection reason.
* @returns {Promise}
*/
this['catch'] = function (onRejected) {
return this.then(undefined, onRejected);
};
}
/**
* The Promise.all(iterable) method returns a promise that resolves when all
* of the promises in the iterable argument have resolved.<br><br>
*
* The result is passed as an array of values from all the promises.
* If something passed in the iterable array is not a promise, it's converted to
* one by Promise.resolve. If any of the passed in promises rejects, the
* all Promise immediately rejects with the value of the promise that rejected,
* discarding all the other promises whether or not they have resolved.
*
* @method all
* @memberOf Promise
* @static
* @param {KeyUsages} promises Array with promises.
* @returns {Promise}
*/
Promise.all = function (promises) {
return new Promise(function (resolve, reject) {
var result = [], count = 0;
function asyncResolve(k) {
count++;
return function (data) {
result[k] = data;
count--;
if (count === 0)
resolve(result);
};
}
function asyncReject(reason) {
if (count > 0)
reject(reason);
count = 0;
}
for (var i = 0, n = promises.length; i < n; i++) {
var data = promises[i];
if (data?.then?.call)
data.then(asyncResolve(i), asyncReject);
else
result[i] = data;
}
if (count === 0)
resolve(result);
});
};
return Promise;
})();
} // </editor-fold>
/*
* Worker executor
*
*/ // <editor-fold defaultstate="collapsed">
var baseUrl = '', nameSuffix = '';
// Try to define from DOM model
if (typeof document !== 'undefined') {
(function () {
var regs = /^(.*)gostCrypto(.*)\.js$/i;
var list = document.querySelectorAll('script');
for (var i = 0, n = list.length; i < n; i++) {
var value = list[i].getAttribute('src');
var test = regs.exec(value);
if (test) {
baseUrl = test[1];
nameSuffix = test[2];
}
}
})();
}
// Local importScripts procedure for include dependens
function importScripts() {
for (var i = 0, n = arguments.length; i < n; i++) {
var name = arguments[i].split('.'),
src = baseUrl + name[0] + nameSuffix + '.' + name[1];
var el = document.querySelector('script[src="' + src + '"]');
if (!el) {
el = document.createElement('script');
el.setAttribute('src', src);
document.head.appendChild(el);
}
}
}
// Create Worker
var worker = false, tasks = [], sequence = 0;
// Worker will create only for first child process and
// Gost implementation libraries not yet loaded
if (!root.importScripts && !root.gostEngine) {
try {
worker = new Worker(baseUrl + 'gostEngine' + nameSuffix + '.js');
// Result of opertion
worker.onmessage = function (event) {
// Find task
var id = event.data.id;
for (var i = 0, n = tasks.length; i < n; i++)
if (tasks[i].id === id)
break;
if (i < n) {
var task = tasks[i];
tasks.splice(i, 1);
// Reject if error or resolve with result
if (event.data.error)
task.reject(new OperationError(event.data.error));
else
task.resolve(event.data.result);
}
};
// Worker error - reject all waiting tasks
worker.onerror = function (event) {
for (var i = 0, n = tasks.length; i < n; i++)
tasks[i].reject(event.error);
tasks = [];
};
} catch (e) {
// Worker is't supported
worker = false;
}
}
if (!root.importScripts) {
// This procedure emulate load dependents as in Worker
root.importScripts = importScripts;
}
if (!worker) {
// Import main module
// Reason: we are already in worker process or Worker interface is not
// yet supported
root.gostEngine || require('./gostEngine');
}
// Executor for any method
function execute(algorithm, method, args) {
return new Promise(function (resolve, reject) {
try {
if (worker) {
var id = ++sequence;
tasks.push({
id: id,
resolve: resolve,
reject: reject
});
worker.postMessage({
id: id, algorithm: algorithm,
method: method, args: args
});
} else {
if (root.gostEngine)
resolve(root.gostEngine.execute(algorithm, method, args));
else
reject(new OperationError('Module gostEngine not found'));
}
} catch (error) {
reject(error);
}
});
}
// Self resolver
function call(callback) {
try {
callback();
} catch (e) {
}
}
// </editor-fold>
/*
* WebCrypto common class references
*
*/ // <editor-fold defaultstate="collapsed">
/**
* The Algorithm object is a dictionary object [WebIDL] which is used to
* specify an algorithm and any additional parameters required to fully
* specify the desired operation.<br>
* <pre>
* dictionary Algorithm {
* DOMString name;
* };
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary}
* @class Algorithm
* @param {DOMString} name The name of the registered algorithm to use.
*/
/**
* AlgorithmIdentifier - Algorithm or DOMString name of algorithm<br>
* <pre>
* typedef (Algorithm or DOMString) AlgorithmIdentifier;
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary}
* @class AlgorithmIdentifier
*/
/**
* The KeyAlgorithm interface represents information about the contents of a
* given Key object.
* <pre>
* interface KeyAlgorithm {
* readonly attribute DOMString name
* };
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-algorithm-interface}
* @class KeyAlgorithm
* @param {DOMString} name The name of the algorithm used to generate the Key
*/
/**
* The type of a key. The recognized key type values are "public", "private"
* and "secret". Opaque keying material, including that used for symmetric
* algorithms, is represented by "secret", while keys used as part of asymmetric
* algorithms composed of public/private keypairs will be either "public" or "private".
* <pre>
* typedef DOMString KeyType;
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface}
* @class KeyType
*/
/**
* Sequence of operation type that may be performed using a key. The recognized
* key usage values are "encrypt", "decrypt", "sign", "verify", "deriveKey",
* "deriveBits", "wrapKey" and "unwrapKey".
* <pre>
* typedef DOMString[] KeyUsages;
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface}
* @class KeyUsages
*/
/**
* The Key object represents an opaque reference to keying material that is
* managed by the user agent.<br>
* This specification provides a uniform interface for many different kinds of
* keying material managed by the user agent. This may include keys that have
* been generated by the user agent, derived from other keys by the user agent,
* imported to the user agent through user actions or using this API,
* pre-provisioned within software or hardware to which the user agent has
* access or made available to the user agent in other ways. The term key refers
* broadly to any keying material including actual keys for cryptographic
* operations and secret values obtained within key derivation or exchange operations.<br>
* The Key object is not required to directly interface with the underlying key
* storage mechanism, and may instead simply be a reference for the user agent
* to understand how to obtain the keying material when needed, eg. when performing
* a cryptographic operation.
* <pre>
* interface Key {
* readonly attribute KeyType type;
* readonly attribute boolean extractable;
* readonly attribute KeyAlgorithm algorithm;
* readonly attribute KeyUsages usages;
* };
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface}
* @class Key
* @param {KeyType} type The type of a key. The recognized key type values are "public", "private" and "secret".
* @param {boolean} extractable Whether or not the raw keying material may be exported by the application.
* @param {KeyAlgorithm} algorithm The Algorithm used to generate the key.
* @param {KeyUsages} usages Key usage array: type of operation that may be performed using a key.
*/
/**
* The KeyPair interface represents an asymmetric key pair that is comprised of both public and private keys.
* <pre>
* interface KeyPair {
* readonly attribute Key publicKey;
* readonly attribute Key privateKey;
* };
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#keypair}
* @class KeyPair
* @param {Key} privateKey Private key
* @param {Key} publicKey Public key
*/
/**
* Specifies a serialization format for a key. The recognized key format values are:
* <ul>
* <li>'raw' - An unformatted sequence of bytes. Intended for secret keys.</li>
* <li>'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.</li>
* <li>'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.</li>
* <li>'jwk' - The key is represented as JSON according to the JSON Web Key format.</li>
* </ul>
* <pre>
* typedef DOMString KeyFormat;
* </pre>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface}
* @class KeyFormat
*/
/**
* Binary data
* <pre>
* typedef (ArrayBuffer or ArrayBufferView) CryptoOperationData;
* </pre>
* @class CryptoOperationData
*/
var CryptoOperationData = ArrayBuffer;
/**
* DER-encoded ArrayBuffer or PEM-encoded DOMString constains ASN.1 object<br>
* <pre>
* typedef (ArrayBuffer or DOMString) FormatedData;
* </pre>
* @class FormatedData
*/
// </editor-fold>
/**
* The gostCrypto provide general purpose cryptographic functionality for
* GOST standards including a cryptographically strong pseudo-random number
* generator seeded with truly random values.
*
* @namespace gostCrypto
*/
var gostCrypto = {};
/**
* The SubtleCrypto class provides low-level cryptographic primitives and algorithms.
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface}
*
* @class SubtleCrypto
*/ // <editor-fold>
function SubtleCrypto() {
}
/**
* The encrypt method returns a new Promise object that will encrypt data
* using the specified algorithm identifier with the supplied Key.
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-encrypt}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST 28147-ECB</b> "prostaya zamena" (ECB) mode (default)</li>
* <li><b>GOST 28147-CFB</b> "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode</li>
* <li><b>GOST 28147-OFB</b> "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode</li>
* <li><b>GOST 28147-CTR</b> "gammirovanie" (counter) mode</li>
* <li><b>GOST 28147-CBC</b> Cipher-Block-Chaining (CBC) mode</li>
* <li><b>GOST R 34.12-ECB</b> "prostaya zamena" (ECB) mode (default)</li>
* <li><b>GOST R 34.12-CFB</b> "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode</li>
* <li><b>GOST R 34.12-OFB</b> "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode</li>
* <li><b>GOST R 34.12-CTR</b> "gammirovanie" (counter) mode</li>
* <li><b>GOST R 34.12-CBC</b> Cipher-Block-Chaining (CBC) mode</li>
* </ul>
* For more information see {@link GostCipher}
*
* @memberOf SubtleCrypto
* @method encrypt
* @instance
* @param {AlgorithmIdentifier} algorithm Algorithm identifier
* @param {Key} key Key object
* @param {CryptoOperationData} data Operation data
* @returns {Promise} Promise that resolves with {@link CryptoOperationData}
*/
SubtleCrypto.prototype.encrypt = function (algorithm, key, data) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.encrypt(algorithm, key, data);
algorithm = normalize(algorithm, 'encrypt');
return execute(algorithm, 'encrypt',
[extractKey('encrypt', algorithm, key), data]);
});
}; // </editor-fold>
/**
* The decrypt method returns a new Promise object that will decrypt data
* using the specified algorithm identifier with the supplied Key.
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-decrypt}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST 28147-ECB</b> "prostaya zamena" (ECB) mode (default)</li>
* <li><b>GOST 28147-CFB</b> "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode</li>
* <li><b>GOST 28147-OFB</b> "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode</li>
* <li><b>GOST 28147-CTR</b> "gammirovanie" (counter) mode</li>
* <li><b>GOST 28147-CBC</b> Cipher-Block-Chaining (CBC) mode</li>
* <li><b>GOST R 34.12-ECB</b> "prostaya zamena" (ECB) mode (default)</li>
* <li><b>GOST R 34.12-CFB</b> "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode</li>
* <li><b>GOST R 34.12-OFB</b> "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode</li>
* <li><b>GOST R 34.12-CTR</b> "gammirovanie" (counter) mode</li>
* <li><b>GOST R 34.12-CBC</b> Cipher-Block-Chaining (CBC) mode</li>
* </ul>
* For additional modes see {@link GostCipher}
*
* @memberOf SubtleCrypto
* @method decrypt
* @instance
* @param {AlgorithmIdentifier} algorithm Algorithm identifier
* @param {Key} key Key object
* @param {CryptoOperationData} data Operation data
* @returns {Promise} Promise that resolves with {@link CryptoOperationData}
*/
SubtleCrypto.prototype.decrypt = function (algorithm, key, data) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.decrypt(algorithm, key, data);
algorithm = normalize(algorithm, 'decrypt');
return execute(algorithm, 'decrypt',
[extractKey('decrypt', algorithm, key), data]);
});
}; // </editor-fold>
/**
* The sign method returns a new Promise object that will sign data using
* the specified algorithm identifier with the supplied Key.
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-sign}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.10-94</b> GOST Signature</li>
* <li><b>GOST R 34.10-94/GOST R 34.11-94</b> GOST Signature with Hash</li>
* <li><b>GOST R 34.10</b> ECGOST Signature</li>
* <li><b>GOST R 34.10/GOST R 34.11-94</b> ECGOST Signature with Old-Style Hash</li>
* <li><b>GOST R 34.10/GOST R 34.11</b> ECGOST Signature with Streebog Hash</li>
* <li><b>GOST 28147-MAC</b> MAC base on GOST 28147</li>
* <li><b>GOST R 34.12-MAC</b> MAC base on GOST R 43.12</li>
* <li><b>GOST R 34.11-HMAC</b> HMAC base on GOST 34.11</li>
* <li><b>SHA-HMAC</b> HMAC base on SHA</li>
* </ul>
* For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
*
* @memberOf SubtleCrypto
* @method sign
* @instance
* @param {AlgorithmIdentifier} algorithm Algorithm identifier
* @param {Key} key Key object
* @param {CryptoOperationData} data Operation data
* @returns {Promise} Promise that resolves with {@link CryptoOperationData}
*/
SubtleCrypto.prototype.sign = function (algorithm, key, data) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.sign(algorithm, key, data);
algorithm = normalize(algorithm, 'sign');
var value = execute(algorithm, 'sign',
[extractKey('sign', algorithm, key), data]).then(function (data) {
if (algorithm.procreator === 'SC' && algorithm.mode === 'SIGN') {
data = gostCrypto.asn1.GostSignature.encode(data);
}
return data;
});
return value;
});
}; // </editor-fold>
/**
* The verify method returns a new Promise object that will verify data
* using the specified algorithm identifier with the supplied Key.
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-verify}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.10-94</b> GOST Signature</li>
* <li><b>GOST R 34.10-94/GOST R 34.11-94</b> GOST Signature with Hash</li>
* <li><b>GOST R 34.10</b> ECGOST Signature</li>
* <li><b>GOST R 34.10/GOST R 34.11-94</b> ECGOST Signature with Old-Style Hash</li>
* <li><b>GOST R 34.10/GOST R 34.11</b> ECGOST Signature with Streebog Hash</li>
* <li><b>GOST 28147-MAC</b> MAC base on GOST 28147</li>
* <li><b>GOST R 34.12-MAC</b> MAC base on GOST R 34.12</li>
* <li><b>GOST R 34.11-HMAC</b> HMAC base on GOST 34.11</li>
* <li><b>SHA-HMAC</b> HMAC base on SHA</li>
* </ul>
* For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
*
* @memberOf SubtleCrypto
* @method verify
* @instance
* @param {AlgorithmIdentifier} algorithm Algorithm identifier
* @param {Key} key Key object
* @param {CryptoOperationData} signature Signature data
* @param {CryptoOperationData} data Operation data
* @returns {Promise} Promise that resolves with boolean value of verification result
*/
SubtleCrypto.prototype.verify = function (algorithm, key, signature, data) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.verify(algorithm, key, signature, data);
algorithm = normalize(algorithm, 'verify');
if (algorithm.procreator === 'SC' && algorithm.mode === 'SIGN') {
var obj = gostCrypto.asn1.GostSignature.decode(signature);
signature = {r: obj.r, s: obj.s};
}
return execute(algorithm, 'verify',
[extractKey('verify', algorithm, key), signature, data]);
});
}; // </editor-fold>
/**
* The digest method returns a new Promise object that will digest data
* using the specified algorithm identifier.
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-digest}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.11-94</b> Old-Style GOST Hash</li>
* <li><b>GOST R 34.11</b> GOST Streebog Hash</li>
* <li><b>SHA</b> SHA Hash</li>
* </ul>
* For additional modes see {@link GostDigest}
*
* @memberOf SubtleCrypto
* @method digest
* @instance
* @param {AlgorithmIdentifier} algorithm Algorithm identifier
* @param {CryptoOperationData} data Operation data
* @returns {Promise} Promise that resolves with {@link CryptoOperationData}
*/
SubtleCrypto.prototype.digest = function (algorithm, data) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.digest(algorithm, data);
algorithm = normalize(algorithm, 'digest');
return execute(algorithm, 'digest', [data]);
});
}; // </editor-fold>
/**
* The generateKey method returns a new Promise object that will key(s) using
* the specified algorithm identifier. Key can be used in according with
* KeyUsages sequence. The recognized key usage values are "encrypt", "decrypt",
* "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey".
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-generateKey}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.10</b> ECGOST Key Pairs</li>
* <li><b>GOST 28147</b> Key for encryption GOST 28147 modes</li>
* <li><b>GOST 28147-KW</b> Key for wrapping GOST 28147 modes</li>
* <li><b>GOST R 34.12</b> Key for encryption GOST R 34.12 modes</li>
* <li><b>GOST R 34.12-KW</b> Key for wrapping GOST R 34.12 modes</li>
* <li><b>GOST R 34.11-KDF</b> Key for Derivation Algorithm</li>
* </ul>
* For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}<br>
* Note: Generation key for GOST R 34.10-94 not supported.
*
* @memberOf SubtleCrypto
* @method generateKey
* @instance
* @param {AlgorithmIdentifier} algorithm Key algorithm identifier
* @param {boolean} extractable Whether or not the raw keying material may be exported by the application
* @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key
* @returns {Promise} Promise that resolves with {@link Key} or {@link KeyPair} in according to key algorithm
*/
SubtleCrypto.prototype.generateKey = function (algorithm, extractable, keyUsages) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.generateKey(algorithm, extractable, keyUsages);
var privateAlgorithm = algorithm.privateKey,
publicAlgorithm = algorithm.publicKey;
algorithm = normalize(algorithm, 'generateKey');
if (privateAlgorithm)
privateAlgorithm = normalize(privateAlgorithm, 'generateKey');
else
privateAlgorithm = algorithm;
if (publicAlgorithm)
publicAlgorithm = normalize(publicAlgorithm, 'generateKey');
else
publicAlgorithm = algorithm;
return execute(algorithm, 'generateKey', []).then(function (data) {
if (data.publicKey && data.privateKey)
return convertKeyPair(publicAlgorithm, privateAlgorithm, extractable, keyUsages, data.publicKey, data.privateKey);
else
return convertKey(algorithm, extractable, keyUsages, data);
});
});
}; // </editor-fold>
/**
* The deriveKey method returns a new Promise object that will key(s) using
* the specified algorithm identifier. Key can be used in according with
* KeyUsage sequence. The recognized key usage values are "encrypt", "decrypt",
* "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey".
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-deriveKey}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.10-DH</b> ECDH Key Agreement mode</li>
* <li><b>GOST R 34.11-KDF</b> Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-PBKDF2</b> Password Based Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-PFXKDF</b> PFX Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-CPKDF</b> Password Based Key for CryptoPro Derivation Algorithm</li>
* <li><b>SHA-PBKDF2</b> Password Based Key for Derivation Algorithm</li>
* <li><b>SHA-PFXKDF</b> PFX Key for Derivation Algorithm</li>
* </ul>
* For additional modes see {@link GostSign} and {@link GostDigest}
*
* @memberOf SubtleCrypto
* @method deriveKey
* @instance
* @param {AlgorithmIdentifier} algorithm Algorithm identifier
* @param {Key} baseKey Derivation key object
* @param {AlgorithmIdentifier} derivedKeyType Derived key algorithm identifier
* @param {boolean} extractable Whether or not the raw keying material may be exported by the application
* @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key
* @returns {Promise} Promise that resolves with {@link Key}
*/
SubtleCrypto.prototype.deriveKey = function (algorithm, baseKey,
derivedKeyType, extractable, keyUsages) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.deriveKey(algorithm, baseKey,
derivedKeyType, extractable, keyUsages);
algorithm = normalize(algorithm, 'deriveKey');
derivedKeyType = normalize(derivedKeyType, 'generateKey');
algorithm.keySize = derivedKeyType.keySize;
if (algorithm['public']) {
algorithm['public'].algorithm = normalize(algorithm['public'].algorithm);
algorithm['public'] = extractKey('deriveKey', algorithm, algorithm['public']);
}
return execute(algorithm, 'deriveKey', [extractKey('deriveKey', algorithm, baseKey)]).then(function (data) {
return convertKey(derivedKeyType, extractable, keyUsages, data);
});
});
}; // </editor-fold>
/**
* The deriveBits method returns length bits on baseKey using the
* specified algorithm identifier.
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-deriveBits}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.10-DH</b> ECDH Key Agreement mode</li>
* <li><b>GOST R 34.11-KDF</b> Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-PBKDF2</b> Password Based Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-PFXKDF</b> PFX Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-CPKDF</b> Password Based Key for CryptoPro Derivation Algorithm</li>
* <li><b>SHA-PBKDF2</b> Password Based Key for Derivation Algorithm</li>
* <li><b>SHA-PFXKDF</b> PFX Key for Derivation Algorithm</li>
* </ul>
* For additional modes see {@link GostSign} and {@link GostDigest}
*
* @memberOf SubtleCrypto
* @method deriveBits
* @instance
* @param {AlgorithmIdentifier} algorithm Algorithm identifier
* @param {Key} baseKey Derivation key object
* @param {number} length Length bits
* @returns {Promise} Promise that resolves with {@link CryptoOperationData}
*/
SubtleCrypto.prototype.deriveBits = function (algorithm, baseKey, length) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.deriveBits(algorithm, baseKey, length);
algorithm = normalize(algorithm, 'deriveBits');
if (algorithm['public'])
algorithm['public'] = extractKey('deriveBits', algorithm, algorithm['public']);
return execute(algorithm, 'deriveBits', [extractKey('deriveBits', algorithm, baseKey), length]);
});
}; // </editor-fold>
/**
* The importKey method returns a new Promise object that will key(s) using
* the specified algorithm identifier. Key can be used in according with
* KeyUsage sequence. The recognized key usage values are "encrypt", "decrypt",
* "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey".<br><br>
* Parameter keyData contains data in defined format.
* The suppored key format values are:
* <ul>
* <li>'raw' - An unformatted sequence of bytes. Intended for secret keys.</li>
* <li>'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.</li>
* <li>'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.</li>
* </ul>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.10-94</b> GOST Private and Public keys</li>
* <li><b>GOST R 34.10</b> ECGOST Private and Public keys</li>
* <li><b>GOST 28147</b> Key for encryption GOST 28147 modes</li>
* <li><b>GOST 28147-KW</b> Key for key wrapping GOST 28147 modes</li>
* <li><b>GOST R 34.12</b> Key for encryption GOST 34.12 modes</li>
* <li><b>GOST R 34.12-KW</b> Key for key wrapping GOST 34.12 modes</li>
* <li><b>GOST R 34.11-KDF</b> Key for Derivation Algorithm</li>
* </ul>
* For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}<br>
*
* @memberOf SubtleCrypto
* @method importKey
* @instance
* @param {KeyFormat} format Key format Format specifies a serialization format for a key
* @param {CryptoOperationData} keyData
* @param {AlgorithmIdentifier} algorithm Key algorithm identifier
* @param {boolean} extractable Whether or not the raw keying material may be exported by the application
* @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key
* @returns {Promise} Promise that resolves with {@link Key}
*/
SubtleCrypto.prototype.importKey = function (format, keyData, algorithm, extractable, keyUsages) // <editor-fold defaultstate="collapsed">
{
var type;
return new Promise(call).then(function () {
if (checkNative(algorithm))
return rootCrypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages);
if (format === 'raw') {
algorithm = normalize(algorithm, 'importKey');
if (keyUsages && keyUsages.indexOf) {
var name = algorithm.name.toUpperCase().replace(/[\.\s]/g, '');
if (name.indexOf('3410') >= 0 && keyUsages.indexOf('sign') >= 0)
type = 'private';
else if (name.indexOf('3410') >= 0 && keyUsages.indexOf('verify') >= 0)
type = 'public';
}
return keyData;
} else {
var key;
if (format === 'pkcs8')
key = gostCrypto.asn1.GostPrivateKeyInfo.decode(keyData).object;
else if (format === 'spki')
key = gostCrypto.asn1.GostSubjectPublicKeyInfo.decode(keyData).object;
else
throw new NotSupportedError('Key format not supported');
algorithm = normalize(key.algorithm, 'importKey');
type = key.type;
if (extractable !== false)
extractable = extractable || key.extractable;
if (keyUsages) {
for (var i = 0; i < keyUsages.length; i++) {
if (key.usages.indexOf(keyUsages[i]) < 0)
throw DataError('Key usage not valid for this key');
}
} else
keyUsages = key.usages;
var data = key.buffer, keySize = algorithm.keySize, dataLen = data.byteLength;
if (type === 'public' || keySize === dataLen)
return data;
else {
// Remove private key masks
if (dataLen % keySize > 0)
throw new DataError('Invalid key size');
algorithm.mode = 'MASK';
algorithm.procreator = 'VN';
var chain = [];
for (var i = keySize; i < dataLen; i += keySize) {
chain.push((function (mask) {
return function (data) {
return execute(algorithm, 'unwrapKey', [mask, data]).then(function (data) {
var next = chain.pop();
if (next)
return next(data);
else {
delete algorithm.mode;
return data;
}
});
};
})(new Uint8Array(data, i, keySize)));
}
return chain.pop()(new Uint8Array(data, 0, keySize));
}
}
}).then(function (data) {
return convertKey(algorithm, extractable, keyUsages, data, type);
});
}; // </editor-fold>
/**
* The exportKey method returns a new Promise object that will key data in
* defined format. <br><br>
* The suppored key format values are:
* <ul>
* <li>'raw' - An unformatted sequence of bytes. Intended for secret keys.</li>
* <li>'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.</li>
* <li>'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.</li>
* </ul>
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST R 34.10-94</b> GOST Private and Public keys</li>
* <li><b>GOST R 34.10</b> ECGOST Private and Public keys</li>
* <li><b>GOST 28147</b> Key for encryption GOST 28147 modes</li>
* <li><b>GOST 28147-KW</b> Key for key wrapping GOST 28147 modes</li>
* <li><b>GOST R 34.12</b> Key for encryption GOST R 34.12 modes</li>
* <li><b>GOST R 34.12-KW</b> Key for key wrapping GOST R 34.12 modes</li>
* <li><b>GOST R 34.11-KDF</b> Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-PBKDF2</b> Import Password for Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-PFXKDF</b> Import PFX Key for Derivation Algorithm</li>
* <li><b>GOST R 34.11-CPKDF</b> Import Password Key for CryptoPro Derivation Algorithm</li>
* <li><b>SHA-PBKDF2</b> Import Password for Key for Derivation Algorithm</li>
* <li><b>SHA-PFXKDF</b> Import PFX Key for Derivation Algorithm</li>
* </ul>
* For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}<br>
*
* @memberOf SubtleCrypto
* @method exportKey
* @instance
* @param {KeyFormat} format Format specifies a serialization format for a key
* @param {Key} key Key object
* @returns {Promise} Promise that resolves with {@link CryptoOperationData}
*/
SubtleCrypto.prototype.exportKey = function (format, key) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (key && checkNative(key.algorithm))
return rootCrypto.subtle.exportKey(format, key);
if (!key.extractable)
throw new InvalidAccessError('Key not extractable');
var raw = extractKey(null, null, key);
if (format === 'raw')
return raw;
else if (format === 'pkcs8' && key?.algorithm?.id) {
if (key.algorithm.procreator === 'VN') {
// Add masks for ViPNet
var algorithm = key.algorithm, mask;
algorithm.mode = 'MASK';
return execute(algorithm, 'generateKey').then(function (data) {
mask = data;
return execute(algorithm, 'wrapKey', [mask, key.buffer]);
}).then(function (data) {
delete algorithm.mode;
var d = new Uint8Array(data.byteLength + mask.byteLength);
d.set(new Uint8Array(data, 0, data.byteLength));
d.set(new Uint8Array(mask, 0, mask.byteLength), data.byteLength);
var buffer = d.buffer;
buffer.enclosed = true;
return gostCrypto.asn1.GostPrivateKeyInfo.encode({
algorithm: algorithm,
buffer: buffer
});
});
} else
return gostCrypto.asn1.GostPrivateKeyInfo.encode(key);
} else if (format === 'spki' && key?.algorithm?.id)
return gostCrypto.asn1.GostSubjectPublicKeyInfo.encode(key);
else
throw new NotSupportedError('Key format not supported');
});
}; // </editor-fold>
/**
* The wrapKey method returns a new Promise object that will wrapped key(s).
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-wrapKey}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST 28147-KW</b> Key Wrapping GOST 28147 modes</li>
* <li><b>GOST R 34.12-KW</b> Key Wrapping GOST R 34.12 modes</li>
* <li><b>GOST 28147-MASK</b> Key Mask GOST 28147 modes</li>
* <li><b>GOST R 34.12-MASK</b> Key Mask GOST R 34.12 modes</li>
* <li><b>GOST R 34.10-MASK</b> Key Mask GOST R 34.10 modes</li>
* </ul>
* For additional modes see {@link GostCipher}<br>
*
* @memberOf SubtleCrypto
* @method wrapKey
* @instance
* @param {KeyFormat} format Format specifies a serialization format for a key. Now suppored only 'raw' key format.
* @param {Key} key Key object
* @param {Key} wrappingKey Wrapping key object
* @param {AlgorithmIdentifier} wrapAlgorithm Algorithm identifier
* @returns {Promise} Promise that resolves with {@link CryptoOperationData}
*/
SubtleCrypto.prototype.wrapKey = function (format, key, wrappingKey, wrapAlgorithm) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(wrapAlgorithm))
return rootCrypto.subtle.wrapKey(format, key, wrappingKey, wrapAlgorithm);
wrapAlgorithm = normalize(wrapAlgorithm, 'wrapKey');
var keyData = extractKey(null, null, key);
if (wrapAlgorithm.procreator === 'SC' && key.type === 'private')
keyData = swapBytes(keyData);
return execute(wrapAlgorithm, 'wrapKey',
[extractKey('wrapKey', wrapAlgorithm, wrappingKey), keyData]).then(function (data) {
if (format === 'raw')
return data;
else
throw new NotSupportedError('Key format not supported');
});
});
}; // </editor-fold>
/**
* The unwrapKey method returns a new Promise object that will unwrapped key(s).
* WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-unwrapKey}<br><br>
*
* Supported algorithm names:
* <ul>
* <li><b>GOST 28147-KW</b> Key Wrapping GOST 28147 modes</li>
* <li><b>GOST R 34.12-KW</b> Key Wrapping GOST R 34.12 modes</li>
* <li><b>GOST 28147-MASK</b> Key Mask GOST 28147 modes</li>
* <li><b>GOST R 34.12-MASK</b> Key Mask GOST R 34.12 modes</li>
* <li><b>GOST R 34.10-MASK</b> Key Mask GOST R 34.10 modes</li>
* </ul>
* For additional modes see {@link GostCipher}<br>
*
* @memberOf SubtleCrypto
* @method unwrapKey
* @instance
* @param {KeyFormat} format Format specifies a serialization format for a key. Now suppored only 'raw' key format.
* @param {CryptoOperationData} wrappedKey Wrapped key data
* @param {Key} unwrappingKey Unwrapping key object
* @param {AlgorithmIdentifier} unwrapAlgorithm Algorithm identifier
* @param {AlgorithmIdentifier} unwrappedKeyAlgorithm Key algorithm identifier
* @param {boolean} extractable Whether or not the raw keying material may be exported by the application
* @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key
* @returns {Promise} Promise that resolves with {@link Key}
*/
SubtleCrypto.prototype.unwrapKey = function (format, wrappedKey, unwrappingKey,
unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages) // <editor-fold defaultstate="collapsed">
{
return new Promise(call).then(function () {
if (checkNative(unwrapAlgorithm))
return rootCrypto.subtle.unwrapKey(format, wrappedKey, unwrappingKey,
unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages);
unwrapAlgorithm = normalize(unwrapAlgorithm, 'unwrapKey');
unwrappedKeyAlgorithm = normalize(unwrappedKeyAlgorithm, 'importKey');
if (format !== 'raw')
throw new NotSupportedError('Key format not supported');
return execute(unwrapAlgorithm, 'unwrapKey', [extractKey('unwrapKey', unwrapAlgorithm, unwrappingKey), wrappedKey]).then(function (data) {
var type;
if (unwrappedKeyAlgorithm && unwrappedKeyAlgorithm.name) {
var name = unwrappedKeyAlgorithm.name.toUpperCase().replace(/[\.\s]/g, '');
if (name.indexOf('3410') >= 0 && keyUsages.indexOf('sign') >= 0)
type = 'private';
else if (name.indexOf('3410') >= 0 && keyUsages.indexOf('verify') >= 0)
type = 'public';
}
if (unwrapAlgorithm.procreator === 'SC' && type === 'private')
data = swapBytes(data);
return convertKey(unwrappedKeyAlgorithm, extractable, keyUsages, data, type);
});
});
}; // </editor-fold>
/**
* The subtle attribute provides an instance of the SubtleCrypto
* interface which provides low-level cryptographic primitives and
* algorithms.
*
* @memberOf gostCrypto
* @type SubtleCrypto
*/
gostCrypto.subtle = new SubtleCrypto();
/**
* The getRandomValues method generates cryptographically random values.
*
* First try to use Web Crypto random genereator. Next make random
* bytes based on standart Math.random mixed with time and mouse pointer
*
* @memberOf gostCrypto
* @param {(CryptoOperationData)} array Destination buffer for random data
*/
gostCrypto.getRandomValues = function (array) // <editor-fold defaultstate="collapsed">
{
// Execute randomizer
GostRandom = GostRandom || root.GostRandom;
var randomSource = GostRandom ? new GostRandom() : rootCrypto;
if (randomSource.getRandomValues)
randomSource.getRandomValues(array);
else
throw new NotSupportedError('Random generator not found');
}; // </editor-fold>
// </editor-fold>
export default gostCrypto;