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
JavaScript
"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