UNPKG

ctjs

Version:

CTjs is a full set of classes necessary to work with any kind of Certificate Transparency log (V1 as from RFC6962, or V2 as from RFC6962-bis). In CTjs you could find all necessary validation/verification functions for all related data shipped with full-fe

338 lines (300 loc) 12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _pvutils = require("pvutils"); var _utils = require("./utils.js"); function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } //************************************************************************************** class MerkleTree { //********************************************************************************** /** * Constructor for MerkleTree class * @param {Object} [parameters={}] * @property {Object} [schema] asn1js parsed value */ constructor(parameters = {}) { //region Internal properties of the object /** * @type {Array.<MerkleTreeLeaf|ArrayBuffer|TransItem>} * @description leafs Array of leafs for the tree */ this.leafs = (0, _pvutils.getParametersValue)(parameters, "leafs", MerkleTree.constants("leafs")); /** * @type {Array.<Array.<ArrayBuffer>>} * @description nodes Array of array of nodes (calculated hashes) for the tree * Each subarray represents next node level. */ this.nodes = (0, _pvutils.getParametersValue)(parameters, "nodes", MerkleTree.constants("nodes")); /** * @type {String} * @description hashName Name of hashing function to apply, default = SHA-256 */ this.hashName = (0, _pvutils.getParametersValue)(parameters, "hashName", MerkleTree.constants("hashName")); //endregion } //********************************************************************************** /** * Return value for a constant by name * @param {string} name String name for a constant */ static constants(name) { switch (name) { case "leafs": case "nodes": return []; case "hashName": return "SHA-256"; default: throw new Error(`Invalid constant name for MerkleTree class: ${name}`); } } //********************************************************************************** /** * Inialize Merkle Tree using provided set of Merkle Tree Leafs * @param {Array.<MerkleTreeLeaf|ArrayBuffer|TransItem>} leafs Array of leafs for the tree * @param {String} [hashName=SHA-256] Name of hashing function to apply, default = SHA-256 * @return {Promise<MerkleTree>} */ static fromLeafs(leafs, hashName = "SHA-256") { return _asyncToGenerator(function* () { //region Initial variables const result = new MerkleTree({ leafs, hashName }); const stack = []; const nodes = []; //endregion //region Initialize first level of nodes for (let i = 0; i < result.leafs.length; i++) { const hash = yield result.getHashByIndex(i); if (i & 1) nodes.push((yield _utils.utils.hashChildren(stack.pop(), hash, result.hashName)));else stack.push(hash); } if (stack.length) nodes.push(stack.pop()); //endregion //region Initialize all nodes yield result.initializeNodes(nodes); //endregion return result; })(); } //********************************************************************************** /** * Inialize nodes in Merkle Tree given provided hashes from previous node's level * @param {Array.<ArrayBuffer>} nodes Array of nodes from previous level * @return {Promise<void>} */ initializeNodes(nodes) { var _this = this; return _asyncToGenerator(function* () { //region Initial variables const stack = []; const level = []; //endregion //region Put new level _this.nodes.push(nodes); if (nodes.length === 1 || nodes.length === 0) return; //endregion //region Initialize another level for (let i = 0; i < nodes.length; i++) { if (i & 1) level.push((yield _utils.utils.hashChildren(stack.pop(), nodes[i], _this.hashName)));else stack.push(nodes[i]); } //endregion //region Push remainder to one level up if (stack.length) { level.push(stack.pop()); _this.nodes[_this.nodes.length - 1].pop(); } //endregion yield _this.initializeNodes(level); })(); } //********************************************************************************** /** * Represent Merkle Tree as a array with BASE-64 encoded values * @return {Promise<[String[]]>} */ asBase64() { var _this2 = this; return _asyncToGenerator(function* () { //region Initial variables const output = [[]]; //endregion //region Store information about all leafs for (let i = 0; i < _this2.leafs.length; i++) output[0].push((0, _pvutils.toBase64)((0, _pvutils.arrayBufferToString)((yield _this2.getHashByIndex(i))))); //endregion //region Store information about all nodes var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _this2.nodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { const level = _step.value; output.push(Array.from(level, function (element) { return (0, _pvutils.toBase64)((0, _pvutils.arrayBufferToString)(element)); })); } //endregion } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return output; })(); } //********************************************************************************** /** * Append array of Merkel Tree Leafs to current set of leafs * @param {Array.<MerkleTreeLeaf|ArrayBuffer|TransItem>} array Array of Merkle Tree Leafs * @return {Promise<void>} */ append(array) { var _this3 = this; return _asyncToGenerator(function* () { const temp = yield MerkleTree.fromLeafs([_this3.leafs, ...array], _this3.hashName); _this3.leafs = temp.leafs.slice(); _this3.nodes = temp.nodes.slice(); })(); } //********************************************************************************** /** * Return hash for element given its index * @param {Number} index Zero-based index of the element * @return {Promise<ArrayBuffer>} */ getHashByIndex(index) { var _this4 = this; return _asyncToGenerator(function* () { //region Check input values if (index > _this4.leafs.length - 1) return new ArrayBuffer(0); //endregion if ("byteLength" in _this4.leafs[index]) return _this4.leafs[index]; return _this4.leafs[index].hash(_this4.hashName); })(); } //********************************************************************************** /** * Get array with inclusion proof given hash of element * @param {ArrayBuffer} hash Hash of existing element * @return {Promise<Array.<ArrayBuffer>>} */ getProofByHash(hash) { var _this5 = this; return _asyncToGenerator(function* () { for (let i = 0; i < _this5.leafs.length; i++) { if ((0, _pvutils.isEqualBuffer)(hash, (yield _this5.getHashByIndex(i)))) return _this5.getProofByIndex(i); } return []; })(); } //********************************************************************************** /** * Get array with inclusion proof given index of element * @param {Number} index Zero-based index of existing element * @return {Promise<Array.<ArrayBuffer>>} */ getProofByIndex(index) { var _this6 = this; return _asyncToGenerator(function* () { return _utils.utils.PATH(index, _this6.leafs, _this6.hashName); })(); } //********************************************************************************** /** * Verifying an Inclusion Proof for any MerkleTreeLeaf with specified index (RFC6962-bis, 2.1.3.2) * @param {ArrayBuffer|MerkleTreeLeaf|TransItem} hash MerkleTreeLeaf object or hash of the MerkleTreeLeaf * @param {Number} treeSize The Merkle tree size to which we need to proof existance * @param {ArrayBuffer} rootHash Hash of the root to compare with * @param {Array.<ArrayBuffer>} proof Array of hashes with proof * @return {Promise<boolean>} */ verifyProofByHash(hash, treeSize, rootHash, proof) { var _this7 = this; return _asyncToGenerator(function* () { for (let i = 0; i < _this7.leafs.length; i++) { if ((0, _pvutils.isEqualBuffer)(hash, (yield _this7.getHashByIndex(i)))) return _this7.verifyProofByIndex(i, treeSize, rootHash, proof); } return false; })(); } //********************************************************************************** /** * Verifying an Inclusion Proof for any MerkleTreeLeaf with specified index (RFC6962-bis, 2.1.3.2) * @param {Number} index Index of MerkleTreeLeaf object * @param {Number} treeSize The Merkle tree size to which we need to proof existance * @param {ArrayBuffer} rootHash Hash of the root to compare with * @param {Array.<ArrayBuffer>} proof Array of hashes with proof * @return {Promise<boolean>} */ verifyProofByIndex(index, treeSize, rootHash, proof) { var _this8 = this; return _asyncToGenerator(function* () { const leafHash = yield _this8.getHashByIndex(index); return _utils.utils.verifyInclusionProof(leafHash, index, treeSize, rootHash, proof, _this8.hashName); })(); } //********************************************************************************** /** * Get consistency proof given size of previous tree to compare with * @param {Number} size Size of previous tree to compare with * @return {Promise<Array.<ArrayBuffer>>} */ getConsistency(size) { var _this9 = this; return _asyncToGenerator(function* () { return _utils.utils.PROOF(size, _this9.leafs, _this9.hashName); })(); } //********************************************************************************** /** * Verifying Consistency between Two Tree Heads (RFC6962-bis, 2.1.4.2) * @param {Number} first First tree size to compare * @param {ArrayBuffer} firstHash Hash of the root for first tree size * @param {Number} second Second tree size to compare * @param {ArrayBuffer} secondHash Hash of the root for second tree size * @param {Array.<ArrayBuffer>} consistency Array of hashes necessary for consistency verification * @return {Promise<boolean>} */ verifyConsistency(first, firstHash, second, secondHash, consistency) { var _this10 = this; return _asyncToGenerator(function* () { return _utils.utils.verifyConsistency(first, firstHash, second, secondHash, consistency, _this10.hashName); })(); } //********************************************************************************** /** * Get hash of the tree head * @return {Promise<ArrayBuffer>} */ getRootHash() { var _this11 = this; return _asyncToGenerator(function* () { return _utils.utils.MTH(_this11.leafs, _this11.hashName); })(); } //********************************************************************************** /** * Calculate a Tree Head Given Proofs (RFC6962-bis, 2.1.3.2) * @param {Number} index Index of the MerkleTreeLeaf * @param {Number} treeSize The Merkle tree size to which we need to proof existance * @param {Array.<ArrayBuffer>} proof Array of hashes with proof * @return {Promise<ArrayBuffer>} */ getRootHashByProof(index, treeSize, proof) { var _this12 = this; return _asyncToGenerator(function* () { const leafHash = yield _this12.getHashByIndex(index); return _utils.utils.calculateRootHashByProof(leafHash, index, treeSize, proof, _this12.hashName); })(); } //********************************************************************************** } exports.default = MerkleTree; //************************************************************************************** //# sourceMappingURL=MerkleTree.js.map