@tangle-js/ld-proofs
Version:
Linked Data Proofs on the Tangle. Powered by IOTA Identity & IOTA Streams
152 lines • 14.1 kB
JavaScript
/* 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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