jsonld-signatures
Version:
An implementation of the Linked Data Signatures specifications for JSON-LD in JavaScript.
194 lines (182 loc) • 7.22 kB
JavaScript
/*!
* Copyright (c) 2010-2023 Digital Bazaar, Inc. All rights reserved.
*/
;
/* Core API */
const api = {};
module.exports = api;
/* API Constants */
const constants = require('./constants');
Object.assign(api, constants);
// TODO: support `ProofChain`
const ProofSet = require('./ProofSet');
const VerificationError = require('./VerificationError');
/**
* Derives a proof from the provided document, resulting in a new document
* with a new `proof` on it as generated by the given cryptographic suite.
*
* @param {object} document - The JSON-LD document from which to derive a
* new proof.
*
* @param {object} options - Options hashmap.
* @param {LinkedDataSignature} options.suite - The linked data signature
* cryptographic suite, containing private key material, with which to sign
* the document.
*
* @param {ProofPurpose} purpose - A proof purpose instance that will
* match proofs to be verified and ensure they were created according to
* the appropriate purpose.
*
* @param {function} documentLoader - A secure document loader (it is
* recommended to use one that provides static known documents, instead of
* fetching from the web) for returning contexts, controller documents, keys,
* and other relevant URLs needed for the proof.
*
* Advanced optional parameters and overrides:
*
* @param {function} [options.expansionMap] - NOT SUPPORTED; do not use.
* @param {boolean} [options.addSuiteContext=true] - Toggles the default
* behavior of each signature suite enforcing the presence of its own
* `@context` (if it is not present, it's added to the context list).
*
* @returns {Promise<object>} Resolves with signed document.
*/
api.derive = async function derive(document, {
suite, purpose, documentLoader, addSuiteContext = true
} = {}) {
if(typeof document !== 'object') {
throw new TypeError('The "document" parameter must be an object.');
}
// Ensure document contains the signature suite specific context URL
// or throw an error (in case an advanced user overrides the
// `addSuiteContext` flag to false).
suite.ensureSuiteContext({document, addSuiteContext});
try {
return await new ProofSet().derive(
document, {suite, purpose, documentLoader});
} catch(e) {
if(!documentLoader && e.name === 'jsonld.InvalidUrl') {
const {details: {url}} = e;
const err = new Error(
`A URL "${url}" could not be fetched; you need to pass ` +
'"documentLoader" or resolve the URL before calling "derive".');
err.cause = e;
throw err;
}
throw e;
}
};
/**
* Cryptographically signs the provided document by adding a `proof` section,
* based on the provided suite and proof purpose.
*
* @param {object} document - The JSON-LD document to be signed.
*
* @param {object} options - Options hashmap.
* @param {LinkedDataSignature} options.suite - The linked data signature
* cryptographic suite with which to sign the document.
*
* @param {ProofPurpose} purpose - A proof purpose instance that will
* match proofs to be verified and ensure they were created according to
* the appropriate purpose.
*
* @param {function} documentLoader - A secure document loader (it is
* recommended to use one that provides static known documents, instead of
* fetching from the web) for returning contexts, controller documents, keys,
* and other relevant URLs needed for the proof.
*
* Advanced optional parameters and overrides:
*
* @param {function} [options.expansionMap] - NOT SUPPORTED; do not use.
* @param {boolean} [options.addSuiteContext=true] - Toggles the default
* behavior of each signature suite enforcing the presence of its own
* `@context` (if it is not present, it's added to the context list).
*
* @returns {Promise<object>} Resolves with signed document.
*/
api.sign = async function sign(document, {
suite, purpose, documentLoader, expansionMap, addSuiteContext = true
} = {}) {
if(expansionMap) {
throw new Error('"expansionMap" not supported.');
}
if(typeof document !== 'object') {
throw new TypeError('The "document" parameter must be an object.');
}
// Ensure document contains the signature suite specific context URL
// or throw an error (in case an advanced user overrides the `addSuiteContext`
// flag to false).
suite.ensureSuiteContext({document, addSuiteContext});
try {
return await new ProofSet().add(document, {suite, purpose, documentLoader});
} catch(e) {
if(!documentLoader && e.name === 'jsonld.InvalidUrl') {
const {details: {url}} = e;
const err = new Error(
`A URL "${url}" could not be fetched; you need to pass ` +
'"documentLoader" or resolve the URL before calling "sign".');
err.cause = e;
throw err;
}
throw e;
}
};
/**
* Verifies the linked data signature on the provided document.
*
* @param {object} document - The JSON-LD document with one or more proofs to be
* verified.
*
* @param {object} options - The options to use.
* @param {LinkedDataSignature|LinkedDataSignature[]} options.suite -
* Acceptable signature suite instances for verifying the proof(s).
*
* @param {ProofPurpose} purpose - A proof purpose instance that will
* match proofs to be verified and ensure they were created according to
* the appropriate purpose.
*
* Advanced optional parameters and overrides:
*
* @param {function} [options.documentLoader] - A custom document loader,
* `Promise<RemoteDocument> documentLoader(url)`.
* @param {function} [options.expansionMap] - NOT SUPPORTED; do not use.
*
* @return {Promise<{verified: boolean, results: Array,
* error: VerificationError}>}
* resolves with an object with a `verified` boolean property that is `true`
* if at least one proof matching the given purpose and suite verifies and
* `false` otherwise; a `results` property with an array of detailed results;
* if `false` an `error` property will be present, with `error.errors`
* containing all of the errors that occurred during the verification process.
*/
api.verify = async function verify(document, {
suite, purpose, documentLoader, expansionMap
} = {}) {
if(expansionMap) {
throw new Error('"expansionMap" not supported.');
}
if(typeof document !== 'object') {
throw new TypeError('The "document" parameter must be an object.');
}
const result = await new ProofSet().verify(
document, {suite, purpose, documentLoader});
const {error} = result;
if(error) {
if(!documentLoader && error.name === 'jsonld.InvalidUrl') {
const {details: {url}} = error;
const urlError = new Error(
`A URL "${url}" could not be fetched; you need to pass ` +
'"documentLoader" or resolve the URL before calling "verify".');
result.error = new VerificationError(urlError);
} else {
result.error = new VerificationError(error);
}
}
return result;
};
// expose suite classes
api.suites = require('./suites').suites;
// expose ProofPurpose classes to enable extensions
api.purposes = require('./purposes').purposes;
// expose document loader helpers
Object.assign(api, require('./documentLoader'));