UNPKG

pkijs

Version:

Public Key Infrastructure (PKI) is the basis of how identity and key management is performed on the web today. PKIjs is a pure JavaScript library implementing the formats that are used in PKI applications. It is built on WebCrypto and aspires to make it p

1,824 lines (1,666 loc) 78.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _asn1js = require("asn1js"); var asn1js = _interopRequireWildcard(_asn1js); var _pvutils = require("pvutils"); var _common = require("./common.js"); var _PublicKeyInfo = require("./PublicKeyInfo.js"); var _PublicKeyInfo2 = _interopRequireDefault(_PublicKeyInfo); var _PrivateKeyInfo = require("./PrivateKeyInfo.js"); var _PrivateKeyInfo2 = _interopRequireDefault(_PrivateKeyInfo); var _AlgorithmIdentifier = require("./AlgorithmIdentifier.js"); var _AlgorithmIdentifier2 = _interopRequireDefault(_AlgorithmIdentifier); var _EncryptedContentInfo = require("./EncryptedContentInfo.js"); var _EncryptedContentInfo2 = _interopRequireDefault(_EncryptedContentInfo); var _RSASSAPSSParams = require("./RSASSAPSSParams.js"); var _RSASSAPSSParams2 = _interopRequireDefault(_RSASSAPSSParams); var _PBKDF2Params = require("./PBKDF2Params.js"); var _PBKDF2Params2 = _interopRequireDefault(_PBKDF2Params); var _PBES2Params = require("./PBES2Params.js"); var _PBES2Params2 = _interopRequireDefault(_PBES2Params); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } //************************************************************************************** /** * Making MAC key using algorithm described in B.2 of PKCS#12 standard. */ function makePKCS12B2Key(cryptoEngine, hashAlgorithm, keyLength, password, salt, iterationCount) { //region Initial variables var u = void 0; var v = void 0; var result = []; //endregion //region Get "u" and "v" values switch (hashAlgorithm.toUpperCase()) { case "SHA-1": u = 20; // 160 v = 64; // 512 break; case "SHA-256": u = 32; // 256 v = 64; // 512 break; case "SHA-384": u = 48; // 384 v = 128; // 1024 break; case "SHA-512": u = 64; // 512 v = 128; // 1024 break; default: throw new Error("Unsupported hashing algorithm"); } //endregion //region Main algorithm making key //region Transform password to UTF-8 like string var passwordViewInitial = new Uint8Array(password); var passwordTransformed = new ArrayBuffer(password.byteLength * 2 + 2); var passwordTransformedView = new Uint8Array(passwordTransformed); for (var i = 0; i < passwordViewInitial.length; i++) { passwordTransformedView[i * 2] = 0x00; passwordTransformedView[i * 2 + 1] = passwordViewInitial[i]; } passwordTransformedView[passwordTransformedView.length - 2] = 0x00; passwordTransformedView[passwordTransformedView.length - 1] = 0x00; password = passwordTransformed.slice(0); //endregion //region Construct a string D (the "diversifier") by concatenating v/8 copies of ID var D = new ArrayBuffer(v); var dView = new Uint8Array(D); for (var _i = 0; _i < D.byteLength; _i++) { dView[_i] = 3; } // The ID value equal to "3" for MACing (see B.3 of standard) //endregion //region Concatenate copies of the salt together to create a string S of length v * ceil(s / v) bytes (the final copy of the salt may be trunacted to create S) var saltLength = salt.byteLength; var sLen = v * Math.ceil(saltLength / v); var S = new ArrayBuffer(sLen); var sView = new Uint8Array(S); var saltView = new Uint8Array(salt); for (var _i2 = 0; _i2 < sLen; _i2++) { sView[_i2] = saltView[_i2 % saltLength]; } //endregion //region Concatenate copies of the password together to create a string P of length v * ceil(p / v) bytes (the final copy of the password may be truncated to create P) var passwordLength = password.byteLength; var pLen = v * Math.ceil(passwordLength / v); var P = new ArrayBuffer(pLen); var pView = new Uint8Array(P); var passwordView = new Uint8Array(password); for (var _i3 = 0; _i3 < pLen; _i3++) { pView[_i3] = passwordView[_i3 % passwordLength]; } //endregion //region Set I=S||P to be the concatenation of S and P var sPlusPLength = S.byteLength + P.byteLength; var I = new ArrayBuffer(sPlusPLength); var iView = new Uint8Array(I); iView.set(sView); iView.set(pView, sView.length); //endregion //region Set c=ceil(n / u) var c = Math.ceil((keyLength >> 3) / u); //endregion //region Initial variables var internalSequence = Promise.resolve(I); //endregion //region For i=1, 2, ..., c, do the following: for (var _i4 = 0; _i4 <= c; _i4++) { internalSequence = internalSequence.then(function (_I) { //region Create contecanetion of D and I var dAndI = new ArrayBuffer(D.byteLength + _I.byteLength); var dAndIView = new Uint8Array(dAndI); dAndIView.set(dView); dAndIView.set(iView, dView.length); //endregion return dAndI; }); //region Make "iterationCount" rounds of hashing for (var j = 0; j < iterationCount; j++) { internalSequence = internalSequence.then(function (roundBuffer) { return cryptoEngine.digest({ name: hashAlgorithm }, new Uint8Array(roundBuffer)); }); } //endregion internalSequence = internalSequence.then(function (roundBuffer) { //region Concatenate copies of Ai to create a string B of length v bits (the final copy of Ai may be truncated to create B) var B = new ArrayBuffer(v); var bView = new Uint8Array(B); for (var _j = 0; _j < B.byteLength; _j++) { bView[_j] = roundBuffer[_j % roundBuffer.length]; } //endregion //region Make new I value var k = Math.ceil(saltLength / v) + Math.ceil(passwordLength / v); var iRound = []; var sliceStart = 0; var sliceLength = v; for (var _j2 = 0; _j2 < k; _j2++) { var chunk = Array.from(new Uint8Array(I.slice(sliceStart, sliceStart + sliceLength))); sliceStart += v; if (sliceStart + v > I.byteLength) sliceLength = I.byteLength - sliceStart; var x = 0x1ff; for (var l = B.byteLength - 1; l >= 0; l--) { x >>= 8; x += bView[l] + chunk[l]; chunk[l] = x & 0xff; } iRound.push.apply(iRound, _toConsumableArray(chunk)); } I = new ArrayBuffer(iRound.length); iView = new Uint8Array(I); iView.set(iRound); //endregion result.push.apply(result, _toConsumableArray(new Uint8Array(roundBuffer))); return I; }); } //endregion //region Initialize final key internalSequence = internalSequence.then(function () { var resultBuffer = new ArrayBuffer(keyLength >> 3); var resultView = new Uint8Array(resultBuffer); resultView.set(new Uint8Array(result).slice(0, keyLength >> 3)); return resultBuffer; }); //endregion //endregion return internalSequence; } //************************************************************************************** /** * Default cryptographic engine for Web Cryptography API */ var CryptoEngine = function () { //********************************************************************************** /** * Constructor for CryptoEngine class * @param {Object} [parameters={}] * @property {Object} [schema] asn1js parsed value */ function CryptoEngine() { var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, CryptoEngine); //region Internal properties of the object /** * @type {Object} * @description Usually here we are expecting "window.crypto" or an equivalent from custom "crypto engine" */ this.crypto = (0, _pvutils.getParametersValue)(parameters, "crypto", {}); /** * @type {Object} * @description Usually here we are expecting "window.crypto.subtle" or an equivalent from custom "crypto engine" */ this.subtle = (0, _pvutils.getParametersValue)(parameters, "subtle", {}); /** * @type {string} * @description Name of the "crypto engine" */ this.name = (0, _pvutils.getParametersValue)(parameters, "name", ""); //endregion } //********************************************************************************** /** * Import WebCrypto keys from different formats * @param {string} format * @param {ArrayBuffer|Uint8Array} keyData * @param {Object} algorithm * @param {boolean} extractable * @param {Array} keyUsages * @returns {Promise} */ _createClass(CryptoEngine, [{ key: "importKey", value: function importKey(format, keyData, algorithm, extractable, keyUsages) { var _this = this; //region Initial variables var jwk = {}; //endregion //region Change "keyData" type if needed if (keyData instanceof Uint8Array) keyData = keyData.buffer; //endregion switch (format.toLowerCase()) { case "raw": return this.subtle.importKey("raw", keyData, algorithm, extractable, keyUsages); case "spki": { var asn1 = asn1js.fromBER(keyData); if (asn1.offset === -1) return Promise.reject("Incorrect keyData"); var publicKeyInfo = new _PublicKeyInfo2.default(); try { publicKeyInfo.fromSchema(asn1.result); } catch (ex) { return Promise.reject("Incorrect keyData"); } // noinspection FallThroughInSwitchStatementJS switch (algorithm.name.toUpperCase()) { case "RSA-PSS": { //region Get information about used hash function switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": jwk.alg = "PS1"; break; case "SHA-256": jwk.alg = "PS256"; break; case "SHA-384": jwk.alg = "PS384"; break; case "SHA-512": jwk.alg = "PS512"; break; default: return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase()); } //endregion } // break omitted case "RSASSA-PKCS1-V1_5": { keyUsages = ["verify"]; // Override existing keyUsages value since the key is a public key jwk.kty = "RSA"; jwk.ext = extractable; jwk.key_ops = keyUsages; if (publicKeyInfo.algorithm.algorithmId !== "1.2.840.113549.1.1.1") return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId); //region Get information about used hash function if ("alg" in jwk === false) { switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": jwk.alg = "RS1"; break; case "SHA-256": jwk.alg = "RS256"; break; case "SHA-384": jwk.alg = "RS384"; break; case "SHA-512": jwk.alg = "RS512"; break; default: return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId); } } //endregion //region Create RSA Public Key elements var publicKeyJSON = publicKeyInfo.toJSON(); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.keys(publicKeyJSON)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var key = _step.value; jwk[key] = publicKeyJSON[key]; } //endregion } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } break; case "ECDSA": keyUsages = ["verify"]; // Override existing keyUsages value since the key is a public key // break omitted case "ECDH": { //region Initial variables jwk = { kty: "EC", ext: extractable, key_ops: keyUsages }; //endregion //region Get information about algorithm if (publicKeyInfo.algorithm.algorithmId !== "1.2.840.10045.2.1") return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId); //endregion //region Create ECDSA Public Key elements var _publicKeyJSON = publicKeyInfo.toJSON(); var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = Object.keys(_publicKeyJSON)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _key = _step2.value; jwk[_key] = _publicKeyJSON[_key]; } //endregion } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } break; case "RSA-OAEP": { jwk.kty = "RSA"; jwk.ext = extractable; jwk.key_ops = keyUsages; if (this.name.toLowerCase() === "safari") jwk.alg = "RSA-OAEP";else { switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": jwk.alg = "RSA-OAEP-1"; break; case "SHA-256": jwk.alg = "RSA-OAEP-256"; break; case "SHA-384": jwk.alg = "RSA-OAEP-384"; break; case "SHA-512": jwk.alg = "RSA-OAEP-512"; break; default: return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId); } } //region Create ECDSA Public Key elements var _publicKeyJSON2 = publicKeyInfo.toJSON(); var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = Object.keys(_publicKeyJSON2)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var _key2 = _step3.value; jwk[_key2] = _publicKeyJSON2[_key2]; } //endregion } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } } break; default: return Promise.reject("Incorrect algorithm name: " + algorithm.name.toUpperCase()); } } break; case "pkcs8": { var privateKeyInfo = new _PrivateKeyInfo2.default(); //region Parse "PrivateKeyInfo" object var _asn = asn1js.fromBER(keyData); if (_asn.offset === -1) return Promise.reject("Incorrect keyData"); try { privateKeyInfo.fromSchema(_asn.result); } catch (ex) { return Promise.reject("Incorrect keyData"); } if ("parsedKey" in privateKeyInfo === false) return Promise.reject("Incorrect keyData"); //endregion // noinspection FallThroughInSwitchStatementJS // noinspection FallThroughInSwitchStatementJS switch (algorithm.name.toUpperCase()) { case "RSA-PSS": { //region Get information about used hash function switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": jwk.alg = "PS1"; break; case "SHA-256": jwk.alg = "PS256"; break; case "SHA-384": jwk.alg = "PS384"; break; case "SHA-512": jwk.alg = "PS512"; break; default: return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase()); } //endregion } // break omitted case "RSASSA-PKCS1-V1_5": { keyUsages = ["sign"]; // Override existing keyUsages value since the key is a private key jwk.kty = "RSA"; jwk.ext = extractable; jwk.key_ops = keyUsages; //region Get information about used hash function if (privateKeyInfo.privateKeyAlgorithm.algorithmId !== "1.2.840.113549.1.1.1") return Promise.reject("Incorrect private key algorithm: " + privateKeyInfo.privateKeyAlgorithm.algorithmId); //endregion //region Get information about used hash function if ("alg" in jwk === false) { switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": jwk.alg = "RS1"; break; case "SHA-256": jwk.alg = "RS256"; break; case "SHA-384": jwk.alg = "RS384"; break; case "SHA-512": jwk.alg = "RS512"; break; default: return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase()); } } //endregion //region Create RSA Private Key elements var privateKeyJSON = privateKeyInfo.toJSON(); var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = Object.keys(privateKeyJSON)[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var _key3 = _step4.value; jwk[_key3] = privateKeyJSON[_key3]; } //endregion } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } } break; case "ECDSA": keyUsages = ["sign"]; // Override existing keyUsages value since the key is a private key // break omitted case "ECDH": { //region Initial variables jwk = { kty: "EC", ext: extractable, key_ops: keyUsages }; //endregion //region Get information about used hash function if (privateKeyInfo.privateKeyAlgorithm.algorithmId !== "1.2.840.10045.2.1") return Promise.reject("Incorrect algorithm: " + privateKeyInfo.privateKeyAlgorithm.algorithmId); //endregion //region Create ECDSA Private Key elements var _privateKeyJSON = privateKeyInfo.toJSON(); var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = Object.keys(_privateKeyJSON)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var _key4 = _step5.value; jwk[_key4] = _privateKeyJSON[_key4]; } //endregion } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } } break; case "RSA-OAEP": { jwk.kty = "RSA"; jwk.ext = extractable; jwk.key_ops = keyUsages; //region Get information about used hash function if (this.name.toLowerCase() === "safari") jwk.alg = "RSA-OAEP";else { switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": jwk.alg = "RSA-OAEP-1"; break; case "SHA-256": jwk.alg = "RSA-OAEP-256"; break; case "SHA-384": jwk.alg = "RSA-OAEP-384"; break; case "SHA-512": jwk.alg = "RSA-OAEP-512"; break; default: return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase()); } } //endregion //region Create RSA Private Key elements var _privateKeyJSON2 = privateKeyInfo.toJSON(); var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = Object.keys(_privateKeyJSON2)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var _key5 = _step6.value; jwk[_key5] = _privateKeyJSON2[_key5]; } //endregion } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6.return) { _iterator6.return(); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } } break; default: return Promise.reject("Incorrect algorithm name: " + algorithm.name.toUpperCase()); } } break; case "jwk": jwk = keyData; break; default: return Promise.reject("Incorrect format: " + format); } //region Special case for Safari browser (since its acting not as WebCrypto standard describes) if (this.name.toLowerCase() === "safari") { // Try to use both ways - import using ArrayBuffer and pure JWK (for Safari Technology Preview) return Promise.resolve().then(function () { return _this.subtle.importKey("jwk", (0, _pvutils.stringToArrayBuffer)(JSON.stringify(jwk)), algorithm, extractable, keyUsages); }).then(function (result) { return result; }, function () { return _this.subtle.importKey("jwk", jwk, algorithm, extractable, keyUsages); }); } //endregion return this.subtle.importKey("jwk", jwk, algorithm, extractable, keyUsages); } //********************************************************************************** /** * Export WebCrypto keys to different formats * @param {string} format * @param {Object} key * @returns {Promise} */ }, { key: "exportKey", value: function exportKey(format, key) { var sequence = this.subtle.exportKey("jwk", key); //region Currently Safari returns ArrayBuffer as JWK thus we need an additional transformation if (this.name.toLowerCase() === "safari") { sequence = sequence.then(function (result) { // Some additional checks for Safari Technology Preview if (result instanceof ArrayBuffer) return JSON.parse((0, _pvutils.arrayBufferToString)(result)); return result; }); } //endregion switch (format.toLowerCase()) { case "raw": return this.subtle.exportKey("raw", key); case "spki": sequence = sequence.then(function (result) { var publicKeyInfo = new _PublicKeyInfo2.default(); try { publicKeyInfo.fromJSON(result); } catch (ex) { return Promise.reject("Incorrect key data"); } return publicKeyInfo.toSchema().toBER(false); }); break; case "pkcs8": sequence = sequence.then(function (result) { var privateKeyInfo = new _PrivateKeyInfo2.default(); try { privateKeyInfo.fromJSON(result); } catch (ex) { return Promise.reject("Incorrect key data"); } return privateKeyInfo.toSchema().toBER(false); }); break; case "jwk": break; default: return Promise.reject("Incorrect format: " + format); } return sequence; } //********************************************************************************** /** * Convert WebCrypto keys between different export formats * @param {string} inputFormat * @param {string} outputFormat * @param {ArrayBuffer|Object} keyData * @param {Object} algorithm * @param {boolean} extractable * @param {Array} keyUsages * @returns {Promise} */ }, { key: "convert", value: function convert(inputFormat, outputFormat, keyData, algorithm, extractable, keyUsages) { var _this2 = this; switch (inputFormat.toLowerCase()) { case "raw": switch (outputFormat.toLowerCase()) { case "raw": return Promise.resolve(keyData); case "spki": return Promise.resolve().then(function () { return _this2.importKey("raw", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("spki", result); }); case "pkcs8": return Promise.resolve().then(function () { return _this2.importKey("raw", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("pkcs8", result); }); case "jwk": return Promise.resolve().then(function () { return _this2.importKey("raw", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("jwk", result); }); default: return Promise.reject("Incorrect outputFormat: " + outputFormat); } case "spki": switch (outputFormat.toLowerCase()) { case "raw": return Promise.resolve().then(function () { return _this2.importKey("spki", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("raw", result); }); case "spki": return Promise.resolve(keyData); case "pkcs8": return Promise.reject("Impossible to convert between SPKI/PKCS8"); case "jwk": return Promise.resolve().then(function () { return _this2.importKey("spki", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("jwk", result); }); default: return Promise.reject("Incorrect outputFormat: " + outputFormat); } case "pkcs8": switch (outputFormat.toLowerCase()) { case "raw": return Promise.resolve().then(function () { return _this2.importKey("pkcs8", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("raw", result); }); case "spki": return Promise.reject("Impossible to convert between SPKI/PKCS8"); case "pkcs8": return Promise.resolve(keyData); case "jwk": return Promise.resolve().then(function () { return _this2.importKey("pkcs8", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("jwk", result); }); default: return Promise.reject("Incorrect outputFormat: " + outputFormat); } case "jwk": switch (outputFormat.toLowerCase()) { case "raw": return Promise.resolve().then(function () { return _this2.importKey("jwk", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("raw", result); }); case "spki": return Promise.resolve().then(function () { return _this2.importKey("jwk", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("spki", result); }); case "pkcs8": return Promise.resolve().then(function () { return _this2.importKey("jwk", keyData, algorithm, extractable, keyUsages); }).then(function (result) { return _this2.exportKey("pkcs8", result); }); case "jwk": return Promise.resolve(keyData); default: return Promise.reject("Incorrect outputFormat: " + outputFormat); } default: return Promise.reject("Incorrect inputFormat: " + inputFormat); } } //********************************************************************************** /** * Wrapper for standard function "encrypt" * @param args * @returns {Promise} */ }, { key: "encrypt", value: function encrypt() { var _subtle; return (_subtle = this.subtle).encrypt.apply(_subtle, arguments); } //********************************************************************************** /** * Wrapper for standard function "decrypt" * @param args * @returns {Promise} */ }, { key: "decrypt", value: function decrypt() { var _subtle2; return (_subtle2 = this.subtle).decrypt.apply(_subtle2, arguments); } //********************************************************************************** /** * Wrapper for standard function "sign" * @param args * @returns {Promise} */ }, { key: "sign", value: function sign() { var _subtle3; return (_subtle3 = this.subtle).sign.apply(_subtle3, arguments); } //********************************************************************************** /** * Wrapper for standard function "verify" * @param args * @returns {Promise} */ }, { key: "verify", value: function verify() { var _subtle4; return (_subtle4 = this.subtle).verify.apply(_subtle4, arguments); } //********************************************************************************** /** * Wrapper for standard function "digest" * @param args * @returns {Promise} */ }, { key: "digest", value: function digest() { var _subtle5; return (_subtle5 = this.subtle).digest.apply(_subtle5, arguments); } //********************************************************************************** /** * Wrapper for standard function "generateKey" * @param args * @returns {Promise} */ }, { key: "generateKey", value: function generateKey() { var _subtle6; return (_subtle6 = this.subtle).generateKey.apply(_subtle6, arguments); } //********************************************************************************** /** * Wrapper for standard function "deriveKey" * @param args * @returns {Promise} */ }, { key: "deriveKey", value: function deriveKey() { var _subtle7; return (_subtle7 = this.subtle).deriveKey.apply(_subtle7, arguments); } //********************************************************************************** /** * Wrapper for standard function "deriveBits" * @param args * @returns {Promise} */ }, { key: "deriveBits", value: function deriveBits() { var _subtle8; return (_subtle8 = this.subtle).deriveBits.apply(_subtle8, arguments); } //********************************************************************************** /** * Wrapper for standard function "wrapKey" * @param args * @returns {Promise} */ }, { key: "wrapKey", value: function wrapKey() { var _subtle9; return (_subtle9 = this.subtle).wrapKey.apply(_subtle9, arguments); } //********************************************************************************** /** * Wrapper for standard function "unwrapKey" * @param args * @returns {Promise} */ }, { key: "unwrapKey", value: function unwrapKey() { var _subtle10; return (_subtle10 = this.subtle).unwrapKey.apply(_subtle10, arguments); } //********************************************************************************** /** * Initialize input Uint8Array by random values (with help from current "crypto engine") * @param {!Uint8Array} view * @returns {*} */ }, { key: "getRandomValues", value: function getRandomValues(view) { if ("getRandomValues" in this.crypto === false) throw new Error("No support for getRandomValues"); return this.crypto.getRandomValues(view); } //********************************************************************************** /** * Get WebCrypto algorithm by wel-known OID * @param {string} oid well-known OID to search for * @returns {Object} */ }, { key: "getAlgorithmByOID", value: function getAlgorithmByOID(oid) { switch (oid) { case "1.2.840.113549.1.1.1": case "1.2.840.113549.1.1.5": return { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-1" } }; case "1.2.840.113549.1.1.11": return { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }; case "1.2.840.113549.1.1.12": return { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-384" } }; case "1.2.840.113549.1.1.13": return { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-512" } }; case "1.2.840.113549.1.1.10": return { name: "RSA-PSS" }; case "1.2.840.113549.1.1.7": return { name: "RSA-OAEP" }; case "1.2.840.10045.2.1": case "1.2.840.10045.4.1": return { name: "ECDSA", hash: { name: "SHA-1" } }; case "1.2.840.10045.4.3.2": return { name: "ECDSA", hash: { name: "SHA-256" } }; case "1.2.840.10045.4.3.3": return { name: "ECDSA", hash: { name: "SHA-384" } }; case "1.2.840.10045.4.3.4": return { name: "ECDSA", hash: { name: "SHA-512" } }; case "1.3.133.16.840.63.0.2": return { name: "ECDH", kdf: "SHA-1" }; case "1.3.132.1.11.1": return { name: "ECDH", kdf: "SHA-256" }; case "1.3.132.1.11.2": return { name: "ECDH", kdf: "SHA-384" }; case "1.3.132.1.11.3": return { name: "ECDH", kdf: "SHA-512" }; case "2.16.840.1.101.3.4.1.2": return { name: "AES-CBC", length: 128 }; case "2.16.840.1.101.3.4.1.22": return { name: "AES-CBC", length: 192 }; case "2.16.840.1.101.3.4.1.42": return { name: "AES-CBC", length: 256 }; case "2.16.840.1.101.3.4.1.6": return { name: "AES-GCM", length: 128 }; case "2.16.840.1.101.3.4.1.26": return { name: "AES-GCM", length: 192 }; case "2.16.840.1.101.3.4.1.46": return { name: "AES-GCM", length: 256 }; case "2.16.840.1.101.3.4.1.4": return { name: "AES-CFB", length: 128 }; case "2.16.840.1.101.3.4.1.24": return { name: "AES-CFB", length: 192 }; case "2.16.840.1.101.3.4.1.44": return { name: "AES-CFB", length: 256 }; case "2.16.840.1.101.3.4.1.5": return { name: "AES-KW", length: 128 }; case "2.16.840.1.101.3.4.1.25": return { name: "AES-KW", length: 192 }; case "2.16.840.1.101.3.4.1.45": return { name: "AES-KW", length: 256 }; case "1.2.840.113549.2.7": return { name: "HMAC", hash: { name: "SHA-1" } }; case "1.2.840.113549.2.9": return { name: "HMAC", hash: { name: "SHA-256" } }; case "1.2.840.113549.2.10": return { name: "HMAC", hash: { name: "SHA-384" } }; case "1.2.840.113549.2.11": return { name: "HMAC", hash: { name: "SHA-512" } }; case "1.2.840.113549.1.9.16.3.5": return { name: "DH" }; case "1.3.14.3.2.26": return { name: "SHA-1" }; case "2.16.840.1.101.3.4.2.1": return { name: "SHA-256" }; case "2.16.840.1.101.3.4.2.2": return { name: "SHA-384" }; case "2.16.840.1.101.3.4.2.3": return { name: "SHA-512" }; case "1.2.840.113549.1.5.12": return { name: "PBKDF2" }; //region Special case - OIDs for ECC curves case "1.2.840.10045.3.1.7": return { name: "P-256" }; case "1.3.132.0.34": return { name: "P-384" }; case "1.3.132.0.35": return { name: "P-521" }; //endregion default: } return {}; } //********************************************************************************** /** * Get OID for each specific algorithm * @param {Object} algorithm * @returns {string} */ }, { key: "getOIDByAlgorithm", value: function getOIDByAlgorithm(algorithm) { var result = ""; switch (algorithm.name.toUpperCase()) { case "RSASSA-PKCS1-V1_5": switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": result = "1.2.840.113549.1.1.5"; break; case "SHA-256": result = "1.2.840.113549.1.1.11"; break; case "SHA-384": result = "1.2.840.113549.1.1.12"; break; case "SHA-512": result = "1.2.840.113549.1.1.13"; break; default: } break; case "RSA-PSS": result = "1.2.840.113549.1.1.10"; break; case "RSA-OAEP": result = "1.2.840.113549.1.1.7"; break; case "ECDSA": switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": result = "1.2.840.10045.4.1"; break; case "SHA-256": result = "1.2.840.10045.4.3.2"; break; case "SHA-384": result = "1.2.840.10045.4.3.3"; break; case "SHA-512": result = "1.2.840.10045.4.3.4"; break; default: } break; case "ECDH": switch (algorithm.kdf.toUpperCase()) {// Non-standard addition - hash algorithm of KDF function case "SHA-1": result = "1.3.133.16.840.63.0.2"; // dhSinglePass-stdDH-sha1kdf-scheme break; case "SHA-256": result = "1.3.132.1.11.1"; // dhSinglePass-stdDH-sha256kdf-scheme break; case "SHA-384": result = "1.3.132.1.11.2"; // dhSinglePass-stdDH-sha384kdf-scheme break; case "SHA-512": result = "1.3.132.1.11.3"; // dhSinglePass-stdDH-sha512kdf-scheme break; default: } break; case "AES-CTR": break; case "AES-CBC": switch (algorithm.length) { case 128: result = "2.16.840.1.101.3.4.1.2"; break; case 192: result = "2.16.840.1.101.3.4.1.22"; break; case 256: result = "2.16.840.1.101.3.4.1.42"; break; default: } break; case "AES-CMAC": break; case "AES-GCM": switch (algorithm.length) { case 128: result = "2.16.840.1.101.3.4.1.6"; break; case 192: result = "2.16.840.1.101.3.4.1.26"; break; case 256: result = "2.16.840.1.101.3.4.1.46"; break; default: } break; case "AES-CFB": switch (algorithm.length) { case 128: result = "2.16.840.1.101.3.4.1.4"; break; case 192: result = "2.16.840.1.101.3.4.1.24"; break; case 256: result = "2.16.840.1.101.3.4.1.44"; break; default: } break; case "AES-KW": switch (algorithm.length) { case 128: result = "2.16.840.1.101.3.4.1.5"; break; case 192: result = "2.16.840.1.101.3.4.1.25"; break; case 256: result = "2.16.840.1.101.3.4.1.45"; break; default: } break; case "HMAC": switch (algorithm.hash.name.toUpperCase()) { case "SHA-1": result = "1.2.840.113549.2.7"; break; case "SHA-256": result = "1.2.840.113549.2.9"; break; case "SHA-384": result = "1.2.840.113549.2.10"; break; case "SHA-512": result = "1.2.840.113549.2.11"; break; default: } break; case "DH": result = "1.2.840.113549.1.9.16.3.5"; break; case "SHA-1": result = "1.3.14.3.2.26"; break; case "SHA-256": result = "2.16.840.1.101.3.4.2.1"; break; case "SHA-384": result = "2.16.840.1.101.3.4.2.2"; break; case "SHA-512": result = "2.16.840.1.101.3.4.2.3"; break; case "CONCAT": break; case "HKDF": break; case "PBKDF2": result = "1.2.840.113549.1.5.12"; break; //region Special case - OIDs for ECC curves case "P-256": result = "1.2.840.10045.3.1.7"; break; case "P-384": result = "1.3.132.0.34"; break; case "P-521": result = "1.3.132.0.35"; break; //endregion default: } return result; } //********************************************************************************** /** * Get default algorithm parameters for each kind of operation * @param {string} algorithmName Algorithm name to get common parameters for * @param {string} operation Kind of operation: "sign", "encrypt", "generatekey", "importkey", "exportkey", "verify" * @returns {*} */ }, { key: "getAlgorithmParameters", value: function getAlgorithmParameters(algorithmName, operation) { var result = { algorithm: {}, usages: [] }; switch (algorithmName.toUpperCase()) { case "RSASSA-PKCS1-V1_5": switch (operation.toLowerCase()) { case "generatekey": result = { algorithm: { name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-256" } }, usages: ["sign", "verify"] }; break; case "verify": case "sign": case "importkey": result = { algorithm: { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }, usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only }; break; case "exportkey": default: return { algorithm: { name: "RSASSA-PKCS1-v1_5" }, usages: [] }; } break; case "RSA-PSS": switch (operation.toLowerCase()) { case "sign": case "verify": result = { algorithm: { name: "RSA-PSS", hash: { name: "SHA-1" }, saltLength: 20 }, usages: ["sign", "verify"] }; break; case "generatekey": result = { algorithm: { name: "RSA-PSS", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-1" } }, usages: ["sign", "verify"] }; break; case "importkey": result = { algorithm: { name: "RSA-PSS", hash: { name: "SHA-1" } }, usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only }; break; case "exportkey": default: return { algorithm: { name: "RSA-PSS" }, usages: [] }; } break; case "RSA-OAEP": switch (operation.toLowerCase()) { case "encrypt": case "decrypt": result = { algorithm: { name: "RSA-OAEP" }, usages: ["encrypt", "decrypt"] }; break; case "generatekey": result = { algorithm: { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-256" } }, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] }; break; case "importkey": result = { algorithm: { name: "RSA-OAEP", hash: { name: "SHA-256" } }, usages: ["encrypt"] // encrypt for "spki" and decrypt for "pkcs8" }; break; case "exportkey": default: return { algorithm: { name: "RSA-OAEP" }, usages: [] }; } break; case "ECDSA": switch (operation.toLowerCase()) { case "generatekey": result = { algorithm: { name: "ECDSA", namedCurve: "P-256" }, usages: ["sign", "verify"] }; break; case "importkey": result = { algorithm: { name: "ECDSA", namedCurve: "P-256" }, usages: ["verify"] // "sign" for "pkcs8" }; break; case "verify": case "sign": result = { algorithm: { name: "ECDSA", hash: { name: "SHA-256" } }, usages: ["sign"] }; break; default: return { algorithm: { name: "ECDSA" }, usages: [] }; } break; case "ECDH": switch (operation.toLowerCase()) { case "exportkey": case "importkey": case "generatekey": result = { algorithm: { name: "ECDH", namedCurve: "P-256" }, usages: ["deriveKey", "deriveBits"] }; break; case "derivekey": case "derivebits": result = { algorithm: { name: "ECDH", namedCurve: "P-256", public: [] // Must be a "publicKey" }, usages: ["encrypt", "decrypt"] }; break; default: return { algorithm: { name: "ECDH" }, usages: [] }; } break; case "AES-CTR": switch (operation.toLowerCase()) { case "importkey": case "exportkey": case "generatekey": result = { algorithm: { name: "AES-CTR", length: 256 }, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] }; break; case "decrypt": case "encrypt": result = { algorithm: { name: "AES-CTR", counter: new Uint8Array(16), length: 10 }, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] }; break; default: return { algorithm: { name: "AES-CTR" }, usages: [] }; } break; case "AES-CBC": switch (operation.toLowerCase()) { case "importkey": case "exportkey": case "generatekey": result = { algorithm: { name: "AES-CBC", length: 256 }, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] }; break; case "decrypt": case "encrypt": result = { algorithm: { name: "AES-CBC", iv: this.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step }, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] }; break; default: return { algorithm: { name: "AES-CBC" }, usages: [] }; } break; case "AES-GCM": switch (operation.toLowerCase()) { case "importkey": case "exportkey": case "generatekey": result = { algorithm: { name: "AES-GCM", length: 256 }, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] }; break; case "decrypt": case "encrypt": result = { algorithm: { name: "AES-GCM", iv: this.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step }, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] }; break; default: return { algorithm: { name: "AES-GCM" }, usages: [] }; } break; case "AES-KW": switch (operation.toLowerCase()) { case "importkey": case "exportkey": case "generatekey": case "wrapkey": case "unwrapkey": result = { algorithm: { name: "AES-KW", length: 256 }, usages: ["wrapKey", "unwrapKey"] }; break; default: return { algorithm: { name: "AES-KW" }, usages: [] }; } break; case "HMAC": switch (operation.toLowerCase()) {