@tangle-js/ld-proofs
Version:
Linked Data Proofs on the Tangle. Powered by IOTA Identity & IOTA Streams
163 lines • 12.7 kB
JavaScript
/* eslint-disable jsdoc/require-jsdoc */
// eslint-disable-next-line unicorn/prefer-node-protocol
import bs58 from "bs58";
import * as crypto from "crypto";
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";
import SigningService from "./services/signingService.mjs";
/**
* It allows to sign and verify messages using a Verification Method provided by a DID.
*
* It generates and verifies EdDSA (Ed25519) signatures.
*
*/
export class IotaSigner {
constructor(did, didDocument) {
this._did = did;
this._didDocument = didDocument;
}
get did() {
return this._did;
}
/**
* Creates a new signer associating it with a particular decentralized identity.
*
* @param did The DID that has the verification methods of the signer.
* @param node The node.
* @returns The newly created signer.
*/
static async create(did, node) {
if (node && !ValidationHelper.url(node)) {
throw new LdProofError(LdProofErrorNames.INVALID_NODE, "Node is not a URL");
}
if (!ValidationHelper.did(did)) {
throw new LdProofError(LdProofErrorNames.INVALID_DID, "Invalid DID");
}
const didDoc = await DidService.resolve(node, did);
return new IotaSigner(did, didDoc);
}
/**
* Signs a string message using the Ed25519 signature algorithm.
*
* @param message The message.
* @param options The signing options.
* @returns The signature details including its value encoded in Base58.
*/
async sign(message, options) {
let secret = options.secret;
if (typeof options.secret === "string") {
secret = bs58.decode(options.secret);
}
const request = {
didDocument: this._didDocument,
type: SignatureTypes.ED25519_2018,
method: options.verificationMethod,
secret: secret,
message
};
const result = await SigningService.sign(request);
return result;
}
/**
* Signs a JSON(-LD) document.
*
* @param doc The JSON(-LD) document as an object or as a string.
* @param options The parameters to use to generate the signature.
* @returns The JSON document including its corresponding Linked Data Signature.
*/
async signJson(doc, options) {
if (options.signatureType === SignatureTypes.JCS_ED25519_2020) {
return this.doSignJson(doc, options);
}
if (options.signatureType === SignatureTypes.ED25519_2018) {
return this.doSignJsonLd(doc, options);
}
// Otherwise exception is thrown
throw new LdProofError(LdProofErrorNames.NOT_SUPPORTED_SIGNATURE, `Only '${SignatureTypes.JCS_ED25519_2020}' and '${SignatureTypes.ED25519_2018}' are supported`);
}
/**
* Signs a JSON document.
*
* @param doc The JSON document as an object or as a string.
* @param options The parameters to use to generate the signature.
* @returns The JSON document including its corresponding Linked Data Signature.
*/
async doSignJson(doc, options) {
const docToBeSigned = JsonHelper.getDocument(doc);
if (options.signatureType !== SignatureTypes.JCS_ED25519_2020) {
throw new LdProofError(LdProofErrorNames.NOT_SUPPORTED_SIGNATURE, "Only the 'JcsEd25519Signature2020' is supported");
}
const proof = {
type: SignatureTypes.JCS_ED25519_2020,
verificationMethod: `${this._didDocument.id()}#${options.verificationMethod}`,
proofPurpose: "dataVerification",
created: new Date().toISOString()
};
// The canonicalization has to be performed over the whole object excluding the proof value
docToBeSigned.proof = proof;
// JSON Canonicalization Scheme
const canonized = JsonCanonicalization.calculate(docToBeSigned);
// We use SHA256 to calculate the digest as mandated by https://identity.foundation/JcsEd25519Signature2020/
const digest = crypto.createHash("sha256").update(canonized)
.digest();
const signature = await this.sign(digest, options);
// Finally restore the original object
delete docToBeSigned.proof;
return {
proofValue: signature.signatureValue,
...proof
};
}
/**
* Signs a JSON-LD document.
*
* @param doc The JSON-LD document as an object or as a string.
* @param options The parameters to use to generate the signature.
* @returns The Linked Data Signature represented as a Linked Data Proof.
*/
async doSignJsonLd(doc, options) {
const docToBeSigned = JsonHelper.getJsonLdDocument(doc);
if (options.signatureType !== SignatureTypes.ED25519_2018) {
throw new LdProofError(LdProofErrorNames.NOT_SUPPORTED_SIGNATURE, "Only the 'Ed25519Signature2018' is supported");
}
const canonizeOptions = {
algorithm: "URDNA2015",
format: "application/n-quads",
documentLoader: customLdContextLoader
};
// RDF canonization algorithm over the document
const canonized = await jsonld.canonize(docToBeSigned, canonizeOptions);
const docHash = crypto
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
.createHash("sha512").update(canonized)
.digest();
const proofOptionsLd = {
"@context": LdContextURL.W3C_SECURITY,
verificationMethod: `${this._didDocument.id()}#${options.verificationMethod}`,
created: new Date().toISOString()
};
const proofOptionsCanonized = await jsonld.canonize(proofOptionsLd, canonizeOptions);
const proofOptionsHash = crypto
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
.createHash("sha512").update(proofOptionsCanonized)
.digest();
const finalHash = Buffer.concat([docHash, proofOptionsHash]);
const signature = await this.sign(finalHash, options);
return {
type: SignatureTypes.ED25519_2018,
verificationMethod: proofOptionsLd.verificationMethod,
proofValue: signature.signatureValue,
proofPurpose: "dataVerification",
created: proofOptionsLd.created
};
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW90YVNpZ25lci5tanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW90YVNpZ25lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx3Q0FBd0M7QUFHeEMsd0RBQXdEO0FBQ3hELE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUN4QixPQUFPLEtBQUssTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUNqQyxPQUFPLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDNUIsT0FBTyxZQUFZLE1BQU0sdUJBQXVCLENBQUM7QUFDakQsT0FBTyxpQkFBaUIsTUFBTSw0QkFBNEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RSxPQUFPLFVBQVUsTUFBTSxzQkFBc0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMvRCxPQUFPLGdCQUFnQixNQUFNLDRCQUE0QixDQUFDO0FBTTFELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDekQsT0FBTyxVQUFVLE1BQU0sdUJBQXVCLENBQUM7QUFDL0MsT0FBTyxjQUFjLE1BQU0sMkJBQTJCLENBQUM7QUFHdkQ7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQUtuQixZQUFvQixHQUFXLEVBQUUsV0FBd0I7UUFDckQsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUM7UUFDaEIsSUFBSSxDQUFDLFlBQVksR0FBRyxXQUFXLENBQUM7SUFDcEMsQ0FBQztJQUVELElBQVcsR0FBRztRQUNWLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQztJQUNyQixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBVyxFQUFFLElBQWE7UUFDakQsSUFBSSxJQUFJLElB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