jsonld-signatures-merkleproof2019
Version:
A jsonld signature implementation to support MerkleProof2019 verification in Verifiable Credential context
270 lines (269 loc) • 12.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LDMerkleProof2019 = void 0;
const lds_merkle_proof_2019_1 = require("@vaultie/lds-merkle-proof-2019");
const jsonld_signatures_1 = __importDefault(require("jsonld-signatures"));
const explorer_lookup_1 = require("@blockcerts/explorer-lookup");
const getTransactionId_1 = __importDefault(require("./helpers/getTransactionId"));
const getChain_1 = __importDefault(require("./helpers/getChain"));
const array_1 = require("./utils/array");
const inspectors_1 = require("./inspectors");
const isMockChain_1 = __importDefault(require("./helpers/isMockChain"));
const VerifierError_1 = __importDefault(require("./models/VerifierError"));
const { LinkedDataProof } = jsonld_signatures_1.default.suites;
class LDMerkleProof2019 extends LinkedDataProof {
constructor({ issuer = null, verificationMethod = null, document = null, proof = null, options = {}, proofPurpose = 'assertionMethod', domain = [], challenge = '' }) {
super({ type: 'MerkleProof2019' });
this.type = 'MerkleProof2019';
this.issuer = null; // TODO: define issuer type
this.verificationMethod = null;
this.proof = null;
this.proofValue = null;
this.document = null;
this.explorerAPIs = [];
this.documentLoader = null;
this.proofVerificationProcess = [
'assertProofValidity',
'getTransactionId',
'computeLocalHash',
'fetchRemoteHash',
'compareHashes',
'checkMerkleRoot',
'checkReceipt'
];
this.identityVerificationProcess = [
'deriveIssuingAddressFromPublicKey',
'ensureVerificationMethodValidity',
'compareIssuingAddress'
];
this.transactionId = '';
if (!document) {
throw new Error('A document signed by MerkleProof2019 is required for the verification process.');
}
this.issuer = issuer;
this.verificationMethod = verificationMethod;
this.document = document;
this.proofPurpose = proofPurpose;
this.domain = Array.isArray(domain) ? domain : [domain];
this.challenge = challenge;
this.setProof(proof);
this.setOptions(options);
this.getChain();
if ((0, isMockChain_1.default)(this.chain)) {
this.adaptProofVerificationProcessToMocknet();
this.adaptIdentityVerificationProcessToMocknet();
}
}
static decodeMerkleProof2019(proof) {
const base58Decoder = new lds_merkle_proof_2019_1.Decoder(proof.proofValue);
return base58Decoder.decode();
}
setProof(externalProof = null) {
const proof = externalProof !== null && externalProof !== void 0 ? externalProof : this.document.proof;
if (!proof) {
throw new Error('The passed document is not signed.');
}
this.proof = proof; // TODO: might be an error if externalProof is not defined and document has multiproof
this.proofValue = LDMerkleProof2019.decodeMerkleProof2019(this.proof);
}
verifyProof() {
return __awaiter(this, arguments, void 0, function* ({ documentLoader, verifyIdentity } = {
documentLoader: (url) => { },
verifyIdentity: true
}) {
this.documentLoader = documentLoader;
let verified;
let error = '';
try {
yield this.verifyProcess(this.proofVerificationProcess);
if (verifyIdentity) {
yield this.verifyIdentity();
}
verified = true;
}
catch (e) {
console.error(e);
verified = false;
error = e.message;
}
return {
verificationMethod: this.verificationMethod,
verified,
error
};
});
}
verifyIdentity() {
return __awaiter(this, void 0, void 0, function* () {
if (this.verificationMethod != null) {
try {
yield this.verifyProcess(this.identityVerificationProcess);
}
catch (e) {
throw new Error(e);
}
}
});
}
getProofVerificationProcess() {
return this.proofVerificationProcess;
}
getIdentityVerificationProcess() {
return this.identityVerificationProcess;
}
getIssuerPublicKey() {
var _a, _b;
if ((0, isMockChain_1.default)(this.chain)) {
return 'This mock chain does not support issuing addresses';
}
return (_b = (_a = this.getTxData()) === null || _a === void 0 ? void 0 : _a.issuingAddress) !== null && _b !== void 0 ? _b : '';
}
getIssuanceTime() {
var _a, _b;
return (_b = (_a = this.getTxData()) === null || _a === void 0 ? void 0 : _a.time) !== null && _b !== void 0 ? _b : '';
}
getChain() {
if (!this.chain) {
this.chain = (0, getChain_1.default)(this.proofValue);
}
return this.chain;
}
getTxData() {
if (!this.txData) {
console.error('Trying to access issuing address when txData not available yet. Did you run the `verify` method yet?');
return null;
}
return this.txData;
}
adaptProofVerificationProcessToMocknet() {
(0, array_1.removeEntry)(this.proofVerificationProcess, 'getTransactionId');
(0, array_1.removeEntry)(this.proofVerificationProcess, 'fetchRemoteHash');
(0, array_1.removeEntry)(this.proofVerificationProcess, 'checkMerkleRoot');
}
adaptIdentityVerificationProcessToMocknet() {
this.identityVerificationProcess = [
'ensureVerificationMethodValidity'
];
}
setOptions(options) {
var _a;
this.explorerAPIs = (_a = options.explorerAPIs) !== null && _a !== void 0 ? _a : [];
if (options.executeStepMethod && typeof options.executeStepMethod === 'function') {
this.executeStep = options.executeStepMethod;
}
}
executeStep(step_1, action_1) {
return __awaiter(this, arguments, void 0, function* (step, action, verificationSuite = '') {
const res = yield action();
return res;
});
}
verifyProcess(process) {
return __awaiter(this, void 0, void 0, function* () {
for (const verificationStep of process) {
if (!this[verificationStep]) {
console.error('verification logic for', verificationStep, 'not implemented');
return;
}
yield this[verificationStep]();
}
});
}
assertProofValidity() {
return __awaiter(this, void 0, void 0, function* () {
yield this.executeStep('assertProofValidity', () => (0, inspectors_1.assertProofValidity)({
expectedProofPurpose: this.proofPurpose,
expectedDomain: this.domain,
expectedChallenge: this.challenge,
proof: this.proof,
issuer: this.issuer
}), this.type // do not remove here or it will break CVJS
);
});
}
checkMerkleRoot() {
return __awaiter(this, void 0, void 0, function* () {
yield this.executeStep('checkMerkleRoot', () => (0, inspectors_1.ensureMerkleRootEqual)(this.proofValue.merkleRoot, this.txData.remoteHash), this.type // do not remove here or it will break CVJS
);
});
}
compareHashes() {
return __awaiter(this, void 0, void 0, function* () {
yield this.executeStep('compareHashes', () => (0, inspectors_1.ensureHashesEqual)(this.localDocumentHash, this.proofValue.targetHash), this.type // do not remove here or it will break CVJS
);
});
}
computeLocalHash() {
return __awaiter(this, void 0, void 0, function* () {
this.localDocumentHash = yield this.executeStep('computeLocalHash', () => __awaiter(this, void 0, void 0, function* () { return yield (0, inspectors_1.computeLocalHash)(this.document, this.proof, this.documentLoader); }), this.type // do not remove here or it will break CVJS
);
});
}
checkReceipt() {
return __awaiter(this, void 0, void 0, function* () {
yield this.executeStep('checkReceipt', () => { (0, inspectors_1.ensureValidReceipt)(this.proofValue); }, this.type);
});
}
getTransactionId() {
return __awaiter(this, void 0, void 0, function* () {
this.transactionId = (0, getTransactionId_1.default)(this.proofValue);
const transactionId = yield this.executeStep('getTransactionId', () => (0, inspectors_1.isTransactionIdValid)(this.transactionId), this.type // do not remove here or it will break CVJS
);
return transactionId;
});
}
fetchRemoteHash() {
return __awaiter(this, void 0, void 0, function* () {
this.txData = yield this.executeStep('fetchRemoteHash', () => __awaiter(this, void 0, void 0, function* () {
var _a;
const txData = yield (0, explorer_lookup_1.lookForTx)({
transactionId: this.transactionId,
chain: (_a = this.chain) === null || _a === void 0 ? void 0 : _a.code,
explorerAPIs: this.explorerAPIs
});
return txData;
}), this.type // do not remove here or it will break CVJS
);
});
}
// ##### DID CORRELATION #####
deriveIssuingAddressFromPublicKey() {
return __awaiter(this, void 0, void 0, function* () {
this.derivedIssuingAddress = yield this.executeStep('deriveIssuingAddressFromPublicKey', () => __awaiter(this, void 0, void 0, function* () { return yield (0, inspectors_1.deriveIssuingAddressFromPublicKey)(this.verificationMethod, this.chain); }), this.type);
});
}
ensureVerificationMethodValidity() {
return __awaiter(this, void 0, void 0, function* () {
yield this.executeStep('ensureVerificationMethodValidity', () => __awaiter(this, void 0, void 0, function* () {
if (this.verificationMethod.expires) {
const expirationDate = new Date(this.verificationMethod.expires).getTime();
if (expirationDate < Date.now()) {
throw new VerifierError_1.default('ensureVerificationMethodValidity', 'The verification key has expired');
}
}
if (this.verificationMethod.revoked) {
// waiting on clarification https://github.com/w3c/cid/issues/152
throw new VerifierError_1.default('ensureVerificationMethodValidity', 'The verification key has been revoked');
}
}), this.type);
});
}
compareIssuingAddress() {
return __awaiter(this, void 0, void 0, function* () {
yield this.executeStep('compareIssuingAddress', () => { (0, inspectors_1.compareIssuingAddress)(this.getIssuerPublicKey(), this.derivedIssuingAddress); }, this.type);
});
}
}
exports.LDMerkleProof2019 = LDMerkleProof2019;