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

540 lines (456 loc) 18.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.setEngine = setEngine; exports.getEngine = getEngine; exports.getCrypto = getCrypto; exports.getRandomValues = getRandomValues; exports.getOIDByAlgorithm = getOIDByAlgorithm; exports.getAlgorithmParameters = getAlgorithmParameters; exports.createCMSECDSASignature = createCMSECDSASignature; exports.stringPrep = stringPrep; exports.createECDSASignatureFromCMS = createECDSASignatureFromCMS; exports.getAlgorithmByOID = getAlgorithmByOID; exports.getHashAlgorithm = getHashAlgorithm; exports.kdfWithCounter = kdfWithCounter; exports.kdf = kdf; var _asn1js = require("asn1js"); var asn1js = _interopRequireWildcard(_asn1js); var _pvutils = require("pvutils"); var _CryptoEngine = require("./CryptoEngine.js"); var _CryptoEngine2 = _interopRequireDefault(_CryptoEngine); 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; } } //************************************************************************************** //region Crypto engine related function //************************************************************************************** var engine = { name: "none", crypto: null, subtle: null }; //************************************************************************************** function setEngine(name, crypto, subtle) { //region We are in Node // noinspection JSUnresolvedVariable if (typeof process !== "undefined") { // noinspection ES6ModulesDependencies, JSUnresolvedVariable if (typeof global[process.pid] === "undefined") { // noinspection JSUnresolvedVariable global[process.pid] = {}; } else { // noinspection JSUnresolvedVariable if (_typeof(global[process.pid]) !== "object") { // noinspection JSUnresolvedVariable throw new Error("Name global." + process.pid + " already exists and it is not an object"); } } // noinspection JSUnresolvedVariable if (typeof global[process.pid].pkijs === "undefined") { // noinspection JSUnresolvedVariable global[process.pid].pkijs = {}; } else { // noinspection JSUnresolvedVariable if (_typeof(global[process.pid].pkijs) !== "object") { // noinspection JSUnresolvedVariable throw new Error("Name global." + process.pid + ".pkijs already exists and it is not an object"); } } // noinspection JSUnresolvedVariable global[process.pid].pkijs.engine = { name: name, crypto: crypto, subtle: subtle }; } //endregion //region We are in browser else { engine = { name: name, crypto: crypto, subtle: subtle }; } //endregion } //************************************************************************************** function getEngine() { //region We are in Node // noinspection JSUnresolvedVariable if (typeof process !== "undefined") { var _engine = void 0; try { // noinspection JSUnresolvedVariable _engine = global[process.pid].pkijs.engine; } catch (ex) { throw new Error("Please call \"setEngine\" before call to \"getEngine\""); } return _engine; } //endregion return engine; } //************************************************************************************** (function initCryptoEngine() { if (typeof self !== "undefined") { if ("crypto" in self) { var engineName = "webcrypto"; /** * Standard crypto object * @type {Object} * @property {Object} [webkitSubtle] Subtle object from Apple */ var cryptoObject = self.crypto; var subtleObject = null; // Apple Safari support if ("webkitSubtle" in self.crypto) { try { subtleObject = self.crypto.webkitSubtle; } catch (ex) { subtleObject = self.crypto.subtle; } engineName = "safari"; } if ("subtle" in self.crypto) subtleObject = self.crypto.subtle; engine = { name: engineName, crypto: cryptoObject, subtle: new _CryptoEngine2.default({ name: engineName, crypto: self.crypto, subtle: subtleObject }) }; } } })(); //************************************************************************************** //endregion //************************************************************************************** //region Declaration of common functions //************************************************************************************** /** * Get crypto subtle from current "crypto engine" or "undefined" * @returns {({decrypt, deriveKey, digest, encrypt, exportKey, generateKey, importKey, sign, unwrapKey, verify, wrapKey}|null)} */ function getCrypto() { var _engine = getEngine(); if (_engine.subtle !== null) return _engine.subtle; return undefined; } //************************************************************************************** /** * Initialize input Uint8Array by random values (with help from current "crypto engine") * @param {!Uint8Array} view * @returns {*} */ function getRandomValues(view) { return getEngine().subtle.getRandomValues(view); } //************************************************************************************** /** * Get OID for each specific algorithm * @param {Object} algorithm * @returns {string} */ function getOIDByAlgorithm(algorithm) { return getEngine().subtle.getOIDByAlgorithm(algorithm); } //************************************************************************************** /** * 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 {*} */ function getAlgorithmParameters(algorithmName, operation) { return getEngine().subtle.getAlgorithmParameters(algorithmName, operation); } //************************************************************************************** /** * Create CMS ECDSA signature from WebCrypto ECDSA signature * @param {ArrayBuffer} signatureBuffer WebCrypto result of "sign" function * @returns {ArrayBuffer} */ function createCMSECDSASignature(signatureBuffer) { //region Initial check for correct length if (signatureBuffer.byteLength % 2 !== 0) return new ArrayBuffer(0); //endregion //region Initial variables var length = signatureBuffer.byteLength / 2; // There are two equal parts inside incoming ArrayBuffer var rBuffer = new ArrayBuffer(length); var rView = new Uint8Array(rBuffer); rView.set(new Uint8Array(signatureBuffer, 0, length)); var rInteger = new asn1js.Integer({ valueHex: rBuffer }); var sBuffer = new ArrayBuffer(length); var sView = new Uint8Array(sBuffer); sView.set(new Uint8Array(signatureBuffer, length, length)); var sInteger = new asn1js.Integer({ valueHex: sBuffer }); //endregion return new asn1js.Sequence({ value: [rInteger.convertToDER(), sInteger.convertToDER()] }).toBER(false); } //************************************************************************************** /** * String preparation function. In a future here will be realization of algorithm from RFC4518 * @param {string} inputString JavaScript string. As soon as for each ASN.1 string type we have a specific transformation function here we will work with pure JavaScript string * @returns {string} Formated string */ function stringPrep(inputString) { //region Initial variables var isSpace = false; var cuttedResult = ""; //endregion var result = inputString.trim(); // Trim input string //region Change all sequence of SPACE down to SPACE char for (var i = 0; i < result.length; i++) { if (result.charCodeAt(i) === 32) { if (isSpace === false) isSpace = true; } else { if (isSpace) { cuttedResult += " "; isSpace = false; } cuttedResult += result[i]; } } //endregion return cuttedResult.toLowerCase(); } //************************************************************************************** /** * Create a single ArrayBuffer from CMS ECDSA signature * @param {Sequence} cmsSignature ASN.1 SEQUENCE contains CMS ECDSA signature * @returns {ArrayBuffer} */ function createECDSASignatureFromCMS(cmsSignature) { //region Check input variables if (cmsSignature instanceof asn1js.Sequence === false) return new ArrayBuffer(0); if (cmsSignature.valueBlock.value.length !== 2) return new ArrayBuffer(0); if (cmsSignature.valueBlock.value[0] instanceof asn1js.Integer === false) return new ArrayBuffer(0); if (cmsSignature.valueBlock.value[1] instanceof asn1js.Integer === false) return new ArrayBuffer(0); //endregion var rValue = cmsSignature.valueBlock.value[0].convertFromDER(); var sValue = cmsSignature.valueBlock.value[1].convertFromDER(); //region Check the lengths of two parts are equal switch (true) { case rValue.valueBlock.valueHex.byteLength < sValue.valueBlock.valueHex.byteLength: { if (sValue.valueBlock.valueHex.byteLength - rValue.valueBlock.valueHex.byteLength !== 1) throw new Error("Incorrect DER integer decoding"); var correctedLength = sValue.valueBlock.valueHex.byteLength; var rValueView = new Uint8Array(rValue.valueBlock.valueHex); var rValueBufferCorrected = new ArrayBuffer(correctedLength); var rValueViewCorrected = new Uint8Array(rValueBufferCorrected); rValueViewCorrected.set(rValueView, 1); rValueViewCorrected[0] = 0x00; // In order to be sure we do not have any garbage here return (0, _pvutils.utilConcatBuf)(rValueBufferCorrected, sValue.valueBlock.valueHex); } case rValue.valueBlock.valueHex.byteLength > sValue.valueBlock.valueHex.byteLength: { if (rValue.valueBlock.valueHex.byteLength - sValue.valueBlock.valueHex.byteLength !== 1) throw new Error("Incorrect DER integer decoding"); var _correctedLength = rValue.valueBlock.valueHex.byteLength; var sValueView = new Uint8Array(sValue.valueBlock.valueHex); var sValueBufferCorrected = new ArrayBuffer(_correctedLength); var sValueViewCorrected = new Uint8Array(sValueBufferCorrected); sValueViewCorrected.set(sValueView, 1); sValueViewCorrected[0] = 0x00; // In order to be sure we do not have any garbage here return (0, _pvutils.utilConcatBuf)(rValue.valueBlock.valueHex, sValueBufferCorrected); } default: { //region In case we have equal length and the length is not even with 2 if (rValue.valueBlock.valueHex.byteLength % 2) { var _correctedLength2 = rValue.valueBlock.valueHex.byteLength + 1; var _rValueView = new Uint8Array(rValue.valueBlock.valueHex); var _rValueBufferCorrected = new ArrayBuffer(_correctedLength2); var _rValueViewCorrected = new Uint8Array(_rValueBufferCorrected); _rValueViewCorrected.set(_rValueView, 1); _rValueViewCorrected[0] = 0x00; // In order to be sure we do not have any garbage here var _sValueView = new Uint8Array(sValue.valueBlock.valueHex); var _sValueBufferCorrected = new ArrayBuffer(_correctedLength2); var _sValueViewCorrected = new Uint8Array(_sValueBufferCorrected); _sValueViewCorrected.set(_sValueView, 1); _sValueViewCorrected[0] = 0x00; // In order to be sure we do not have any garbage here return (0, _pvutils.utilConcatBuf)(_rValueBufferCorrected, _sValueBufferCorrected); } //endregion } } //endregion return (0, _pvutils.utilConcatBuf)(rValue.valueBlock.valueHex, sValue.valueBlock.valueHex); } //************************************************************************************** /** * Get WebCrypto algorithm by wel-known OID * @param {string} oid well-known OID to search for * @returns {Object} */ function getAlgorithmByOID(oid) { return getEngine().subtle.getAlgorithmByOID(oid); } //************************************************************************************** /** * Getting hash algorithm by signature algorithm * @param {AlgorithmIdentifier} signatureAlgorithm Signature algorithm * @returns {string} */ function getHashAlgorithm(signatureAlgorithm) { return getEngine().subtle.getHashAlgorithm(signatureAlgorithm); } //************************************************************************************** /** * ANS X9.63 Key Derivation Function having a "Counter" as a parameter * @param {string} hashFunction Used hash function * @param {ArrayBuffer} Zbuffer ArrayBuffer containing ECDH shared secret to derive from * @param {number} Counter * @param {ArrayBuffer} SharedInfo Usually DER encoded "ECC_CMS_SharedInfo" structure */ function kdfWithCounter(hashFunction, Zbuffer, Counter, SharedInfo) { //region Check of input parameters switch (hashFunction.toUpperCase()) { case "SHA-1": case "SHA-256": case "SHA-384": case "SHA-512": break; default: return Promise.reject("Unknown hash function: " + hashFunction); } if (Zbuffer instanceof ArrayBuffer === false) return Promise.reject("Please set \"Zbuffer\" as \"ArrayBuffer\""); if (Zbuffer.byteLength === 0) return Promise.reject("\"Zbuffer\" has zero length, error"); if (SharedInfo instanceof ArrayBuffer === false) return Promise.reject("Please set \"SharedInfo\" as \"ArrayBuffer\""); if (Counter > 255) return Promise.reject("Please set \"Counter\" variable to value less or equal to 255"); //endregion //region Initial variables var counterBuffer = new ArrayBuffer(4); var counterView = new Uint8Array(counterBuffer); counterView[0] = 0x00; counterView[1] = 0x00; counterView[2] = 0x00; counterView[3] = Counter; var combinedBuffer = new ArrayBuffer(0); //endregion //region Get a "crypto" extension var crypto = getCrypto(); if (typeof crypto === "undefined") return Promise.reject("Unable to create WebCrypto object"); //endregion //region Create a combined ArrayBuffer for digesting combinedBuffer = (0, _pvutils.utilConcatBuf)(combinedBuffer, Zbuffer); combinedBuffer = (0, _pvutils.utilConcatBuf)(combinedBuffer, counterBuffer); combinedBuffer = (0, _pvutils.utilConcatBuf)(combinedBuffer, SharedInfo); //endregion //region Return digest of combined ArrayBuffer and information about current counter return crypto.digest({ name: hashFunction }, combinedBuffer).then(function (result) { return { counter: Counter, result: result }; }); //endregion } //************************************************************************************** /** * ANS X9.63 Key Derivation Function * @param {string} hashFunction Used hash function * @param {ArrayBuffer} Zbuffer ArrayBuffer containing ECDH shared secret to derive from * @param {number} keydatalen Length (!!! in BITS !!!) of used kew derivation function * @param {ArrayBuffer} SharedInfo Usually DER encoded "ECC_CMS_SharedInfo" structure */ function kdf(hashFunction, Zbuffer, keydatalen, SharedInfo) { //region Initial variables var hashLength = 0; var maxCounter = 1; var kdfArray = []; //endregion //region Check of input parameters switch (hashFunction.toUpperCase()) { case "SHA-1": hashLength = 160; // In bits break; case "SHA-256": hashLength = 256; // In bits break; case "SHA-384": hashLength = 384; // In bits break; case "SHA-512": hashLength = 512; // In bits break; default: return Promise.reject("Unknown hash function: " + hashFunction); } if (Zbuffer instanceof ArrayBuffer === false) return Promise.reject("Please set \"Zbuffer\" as \"ArrayBuffer\""); if (Zbuffer.byteLength === 0) return Promise.reject("\"Zbuffer\" has zero length, error"); if (SharedInfo instanceof ArrayBuffer === false) return Promise.reject("Please set \"SharedInfo\" as \"ArrayBuffer\""); //endregion //region Calculated maximum value of "Counter" variable var quotient = keydatalen / hashLength; if (Math.floor(quotient) > 0) { maxCounter = Math.floor(quotient); if (quotient - maxCounter > 0) maxCounter++; } //endregion //region Create an array of "kdfWithCounter" for (var i = 1; i <= maxCounter; i++) { kdfArray.push(kdfWithCounter(hashFunction, Zbuffer, i, SharedInfo)); } //endregion //region Return combined digest with specified length return Promise.all(kdfArray).then(function (incomingResult) { //region Initial variables var combinedBuffer = new ArrayBuffer(0); var currentCounter = 1; var found = true; //endregion //region Combine all buffer together while (found) { found = false; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = incomingResult[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var result = _step.value; if (result.counter === currentCounter) { combinedBuffer = (0, _pvutils.utilConcatBuf)(combinedBuffer, result.result); found = true; break; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } currentCounter++; } //endregion //region Create output buffer with specified length keydatalen >>= 3; // Divide by 8 since "keydatalen" is in bits if (combinedBuffer.byteLength > keydatalen) { var newBuffer = new ArrayBuffer(keydatalen); var newView = new Uint8Array(newBuffer); var combinedView = new Uint8Array(combinedBuffer); for (var _i = 0; _i < keydatalen; _i++) { newView[_i] = combinedView[_i]; }return newBuffer; } return combinedBuffer; // Since the situation when "combinedBuffer.byteLength < keydatalen" here we have only "combinedBuffer.byteLength === keydatalen" //endregion }); //endregion } //************************************************************************************** //endregion //************************************************************************************** //# sourceMappingURL=common.js.map