UNPKG

@tangle-js/ld-proofs

Version:

Linked Data Proofs on the Tangle. Powered by IOTA Identity & IOTA Streams

152 lines 14.1 kB
/* eslint-disable jsdoc/require-jsdoc */ import bs58 from "bs58"; // eslint-disable-next-line unicorn/prefer-node-protocol import * as crypto from "crypto"; import pkg from "elliptic"; import jsonld from "jsonld"; import LdProofError from "./errors/ldProofError.mjs"; import LdProofErrorNames from "./errors/ldProofErrorNames.mjs"; import { JsonCanonicalization } from "./helpers/jsonCanonicalization.mjs"; import JsonHelper from "./helpers/jsonHelper.mjs"; import { customLdContextLoader } from "./helpers/jsonLdHelper.mjs"; import ValidationHelper from "./helpers/validationHelper.mjs"; import { LdContextURL } from "./models/ldContextURL.mjs"; import { SignatureTypes } from "./models/signatureTypes.mjs"; import DidService from "./services/didService.mjs"; // eslint-disable-next-line @typescript-eslint/naming-convention const { eddsa: EdDSA } = pkg; export class IotaVerifier { /** * Verifies a Ed25519 signature corresponding to a string message. * * @param message The message to be verified. * @param signatureValue The signature value. * @param options The verification request. * @returns True or false depending on the verification result. */ static async verify(message, signatureValue, options) { if (options.node && !ValidationHelper.url(options.node)) { throw new LdProofError(LdProofErrorNames.INVALID_NODE, "The node has to be a URL"); } if (!ValidationHelper.did(options.verificationMethod)) { throw new LdProofError(LdProofErrorNames.INVALID_DID, "Invalid DID"); } const resolution = await DidService.resolveMethod(options?.node, options.verificationMethod); if (resolution.type().toString() !== "Ed25519VerificationKey2018") { throw new LdProofError(LdProofErrorNames.INVALID_VERIFICATION_METHOD, "Only 'Ed25519VerificationKey2018' verification methods are allowed"); } const publicKey = DidService.extractPublicKey(resolution); // Only works with multibase keys which are actually encoded using Base58 return this.verifySignature(signatureValue, message, publicKey); } /** * Verifies a JSON(-LD) document containing a Linked Data Signature. * * @param doc The document to verify. * @param options The verification options. * @returns True or false depending on the verification result. */ static async verifyJson(doc, options) { const document = JsonHelper.getSignedDocument(doc); if (document.proof.type === SignatureTypes.JCS_ED25519_2020) { return this.doVerifyJson(document, options); } if (document.proof.type === SignatureTypes.ED25519_2018) { return this.doVerifyJsonLd(document, options); } // Otherwise exception is thrown throw new LdProofError(LdProofErrorNames.NOT_SUPPORTED_SIGNATURE, `Only '${SignatureTypes.JCS_ED25519_2020}' and '${SignatureTypes.ED25519_2018}' are supported`); } /** * Verifies a JSON document containing a Linked Data Signature. * * @param doc The document to verify. * @param options The verification options. * @returns True or false depending on the verification result. */ static async doVerifyJson(doc, options) { const document = JsonHelper.getSignedDocument(doc); const resolution = await this.verificationMethod(document, options?.node); const proof = document.proof; // After removing the proofValue we obtain the canonical form and that will be verified const proofValue = proof.proofValue; delete proof.proofValue; const canonical = JsonCanonicalization.calculate(document); const msgHash = crypto .createHash("sha256").update(canonical) .digest(); const publicKey = DidService.extractPublicKey(resolution); const result = this.verifySignature(proofValue, msgHash, publicKey); // Restore the proof value proof.proofValue = proofValue; return result; } /** * Verifies a JSON-LD document containing a Linked Data Signature. * * @param doc The document to be verified. * @param options The verification options. * @returns True or false depending on the verifi.cation result. */ static async doVerifyJsonLd(doc, options) { const document = JsonHelper.getSignedJsonLdDocument(doc); const resolution = await this.verificationMethod(document, options?.node); const proof = document.proof; const proofOptions = { "@context": LdContextURL.W3C_SECURITY, verificationMethod: proof.verificationMethod, created: proof.created }; // Remove the document proof to calculate the canonization without the proof delete document.proof; const canonizeOptions = { algorithm: "URDNA2015", format: "application/n-quads", documentLoader: customLdContextLoader }; const docCanonical = await jsonld.canonize(document, canonizeOptions); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const docHash = crypto.createHash("sha512").update(docCanonical) .digest(); const proofCanonical = await jsonld.canonize(proofOptions, canonizeOptions); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const proofHash = crypto.createHash("sha512").update(proofCanonical) .digest(); const hashToVerify = Buffer.concat([docHash, proofHash]); const publicKey = DidService.extractPublicKey(resolution); const result = this.verifySignature(proof.proofValue, hashToVerify, publicKey); // Restore the proof value on the original document document.proof = proof; return result; } static async verificationMethod(document, node) { if (node && !ValidationHelper.url(node)) { throw new LdProofError(LdProofErrorNames.INVALID_NODE, "The node has to be a URL"); } const proof = document.proof; const verificationMethod = proof.verificationMethod; if (!ValidationHelper.did(verificationMethod)) { throw new LdProofError(LdProofErrorNames.INVALID_DID, "Invalid DID"); } const resolution = await DidService.resolveMethod(node, verificationMethod); if (resolution.type().toString() !== "Ed25519VerificationKey2018") { throw new LdProofError(LdProofErrorNames.INVALID_VERIFICATION_METHOD, "Only 'Ed25519VerificationKey2018' verification methods are allowed"); } return resolution; } static verifySignature(signature, message, publicKeyBase58) { try { const signatureBytes = bs58.decode(signature); const publicKeyBytes = bs58.decode(publicKeyBase58); const ed25519 = new EdDSA("ed25519"); const ecKey = ed25519.keyFromPublic(publicKeyBytes.toString("hex"), "hex"); return (ecKey.verify(message, signatureBytes.toString("hex"))); } catch (error) { // eslint-disable-next-line no-console console.error("Error while verifying signature:", error); return false; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW90YVZlcmlmaWVyLm1qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pb3RhVmVyaWZpZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0NBQXdDO0FBR3hDLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUN4Qix3REFBd0Q7QUFDeEQsT0FBTyxLQUFLLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDakMsT0FBTyxHQUFHLE1BQU0sVUFBVSxDQUFDO0FBQzNCLE9BQU8sTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUM1QixPQUFPLFlBQVksTUFBTSx1QkFBdUIsQ0FBQztBQUNqRCxPQUFPLGlCQUFpQixNQUFNLDRCQUE0QixDQUFDO0FBQzNELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3RFLE9BQU8sVUFBVSxNQUFNLHNCQUFzQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQy9ELE9BQU8sZ0JBQWdCLE1BQU0sNEJBQTRCLENBQUM7QUFJMUQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3JELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RCxPQUFPLFVBQVUsTUFBTSx1QkFBdUIsQ0FBQztBQUUvQyxnRUFBZ0U7QUFDaEUsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsR0FBRyxHQUFHLENBQUM7QUFFN0IsTUFBTSxPQUFPLFlBQVk7SUFDckI7Ozs7Ozs7T0FPRztJQUNJLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQWUsRUFBRSxjQUFzQixFQUM5RCxPQUE2QjtRQUM3QixJQUFJLE9BQU8sQ0FBQyxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ3JELE1BQU0sSUFBSSxZQUFZLENBQUMsaUJBQWlCLENB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