UNPKG

jsonld-signatures-merkleproof2019

Version:

A jsonld signature implementation to support MerkleProof2019 verification in Verifiable Credential context

263 lines (262 loc) 11.7 kB
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()); }); }; import { Decoder } from '@vaultie/lds-merkle-proof-2019'; import jsigs from 'jsonld-signatures'; import { lookForTx } from '@blockcerts/explorer-lookup'; import getTransactionId from './helpers/getTransactionId'; import getChain from './helpers/getChain'; import { removeEntry } from './utils/array'; import { assertProofValidity, isTransactionIdValid, computeLocalHash, ensureHashesEqual, ensureMerkleRootEqual, ensureValidReceipt, deriveIssuingAddressFromPublicKey, compareIssuingAddress } from './inspectors'; import isMockChain from './helpers/isMockChain'; import VerifierError from './models/VerifierError'; const { LinkedDataProof } = jsigs.suites; export 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 (isMockChain(this.chain)) { this.adaptProofVerificationProcessToMocknet(); this.adaptIdentityVerificationProcessToMocknet(); } } static decodeMerkleProof2019(proof) { const base58Decoder = new 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 (isMockChain(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 = getChain(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() { removeEntry(this.proofVerificationProcess, 'getTransactionId'); removeEntry(this.proofVerificationProcess, 'fetchRemoteHash'); 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', () => 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', () => 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', () => 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 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', () => { ensureValidReceipt(this.proofValue); }, this.type); }); } getTransactionId() { return __awaiter(this, void 0, void 0, function* () { this.transactionId = getTransactionId(this.proofValue); const transactionId = yield this.executeStep('getTransactionId', () => 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 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 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('ensureVerificationMethodValidity', 'The verification key has expired'); } } if (this.verificationMethod.revoked) { // waiting on clarification https://github.com/w3c/cid/issues/152 throw new VerifierError('ensureVerificationMethodValidity', 'The verification key has been revoked'); } }), this.type); }); } compareIssuingAddress() { return __awaiter(this, void 0, void 0, function* () { yield this.executeStep('compareIssuingAddress', () => { compareIssuingAddress(this.getIssuerPublicKey(), this.derivedIssuingAddress); }, this.type); }); } }