Skip to content

Commit

Permalink
Use jsonld@7 and safe mode instead of expansionMap.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlongley committed Aug 23, 2022
1 parent cb43dd7 commit d17c390
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 105 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# jsonld-signatures ChangeLog

## 11.0.0 - 2022-08-xx

### Changed
- **BREAKING**: Use `jsonld@7` to make use of `safe` mode. The `safe` mode
is a more robust replacement for `expansionMap`.

### Removed
- **BREAKING**: Remove `expansionMap` option. Now `safe` mode is used internally
instead.

## 10.0.0 - 2022-06-06

### Changed
Expand Down
56 changes: 16 additions & 40 deletions lib/ProofSet.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/*!
* Copyright (c) 2018 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';

const constants = require('./constants');
const jsonld = require('jsonld');
const {extendContextLoader, strictDocumentLoader} = require('./documentLoader');
const {serializeError} = require('serialize-error');
const strictExpansionMap = require('./expansionMap');

module.exports = class ProofSet {
/**
Expand Down Expand Up @@ -35,16 +34,11 @@ module.exports = class ProofSet {
*
* @param [documentLoader] {function} a custom document loader,
* `Promise<RemoteDocument> documentLoader(url)`.
* @param [expansionMap] {function} A custom expansion map that is
* passed to the JSON-LD processor; by default a function that will throw
* an error when unmapped properties are detected in the input, use `false`
* to turn this off and allow unmapped properties to be dropped or use a
* custom function.
*
* @return {Promise<object>} resolves with the signed document, with
* the signature in the top-level `proof` property.
*/
async add(document, {suite, purpose, documentLoader, expansionMap} = {}) {
async add(document, {suite, purpose, documentLoader} = {}) {
if(!suite) {
throw new TypeError('"options.suite" is required.');
}
Expand All @@ -57,11 +51,6 @@ module.exports = class ProofSet {
} else {
documentLoader = strictDocumentLoader;
}
if(expansionMap === undefined) {
expansionMap = strictExpansionMap;
} else if(expansionMap === false) {
expansionMap = undefined;
}

// preprocess document to prepare to remove existing proofs
// let input;
Expand All @@ -73,7 +62,7 @@ module.exports = class ProofSet {
// create the new proof (suites MUST output a proof using the security-v2
// `@context`)
const proof = await suite.createProof({
document: input, purpose, documentLoader, expansionMap
document: input, purpose, documentLoader
});

jsonld.addValue(document, 'proof', proof);
Expand Down Expand Up @@ -103,19 +92,14 @@ module.exports = class ProofSet {
*
* @param {function} [documentLoader] a custom document loader,
* `Promise<RemoteDocument> documentLoader(url)`.
* @param {function} [expansionMap] - A custom expansion map that is
* passed to the JSON-LD processor; by default a function that will throw
* an error when unmapped properties are detected in the input, use `false`
* to turn this off and allow unmapped properties to be dropped or use a
* custom function.
*
* @return {Promise<{verified: boolean, results: Array, error: *}>} 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.
*/
async verify(document, {suite, purpose, documentLoader, expansionMap} = {}) {
async verify(document, {suite, purpose, documentLoader} = {}) {
if(!suite) {
throw new TypeError('"options.suite" is required.');
}
Expand All @@ -132,26 +116,19 @@ module.exports = class ProofSet {
} else {
documentLoader = strictDocumentLoader;
}
if(expansionMap === undefined) {
expansionMap = strictExpansionMap;
} else if(expansionMap === false) {
expansionMap = undefined;
}

try {
// shallow copy to allow for removal of proof set prior to canonize
document = {...document};

// get proofs from document
const {proofSet, document: doc} = await _getProofs({
document, documentLoader, expansionMap
});
const {proofSet, document: doc} = await _getProofs(
{document, documentLoader});
document = doc;

// verify proofs
const results = await _verify({
document, suites, proofSet, purpose, documentLoader, expansionMap
});
const results = await _verify(
{document, suites, proofSet, purpose, documentLoader});
if(results.length === 0) {
const error = new Error(
'Did not verify any proofs; insufficient proofs matched the ' +
Expand Down Expand Up @@ -201,7 +178,7 @@ async function _getProofs({document}) {
}

async function _verify({
document, suites, proofSet, purpose, documentLoader, expansionMap
document, suites, proofSet, purpose, documentLoader
}) {
// map each purpose to at least one proof to verify
const purposes = Array.isArray(purpose) ? purpose : [purpose];
Expand All @@ -210,7 +187,7 @@ async function _verify({
const suiteMatchQueue = new Map();
await Promise.all(purposes.map(purpose => _matchProofSet({
purposeToProofs, proofToSuite, purpose, proofSet, suites,
suiteMatchQueue, document, documentLoader, expansionMap
suiteMatchQueue, document, documentLoader
})));

// every purpose must have at least one matching proof or verify will fail
Expand All @@ -233,9 +210,8 @@ async function _verify({
return {valid: true};
}
};
const {verified, verificationMethod, error} = await suite.verifyProof({
proof, document, purpose, documentLoader, expansionMap
});
const {verified, verificationMethod, error} = await suite.verifyProof(
{proof, document, purpose, documentLoader});
if(!vm) {
vm = verificationMethod;
}
Expand Down Expand Up @@ -268,7 +244,7 @@ async function _verify({
let purposeResult;
try {
purposeResult = await purpose.validate(proof, {
document, suite, verificationMethod, documentLoader, expansionMap
document, suite, verificationMethod, documentLoader
});
} catch(error) {
purposeResult = {valid: false, error};
Expand Down Expand Up @@ -316,11 +292,11 @@ function _makeSerializable(error) {

async function _matchProofSet({
purposeToProofs, proofToSuite, purpose, proofSet, suites,
suiteMatchQueue, document, documentLoader, expansionMap
suiteMatchQueue, document, documentLoader
}) {
for(const proof of proofSet) {
// first check if the proof matches the purpose; if it doesn't continue
if(!await purpose.match(proof, {document, documentLoader, expansionMap})) {
if(!await purpose.match(proof, {document, documentLoader})) {
continue;
}

Expand All @@ -339,7 +315,7 @@ async function _matchProofSet({
}
let promise = matchingProofs.get(proof);
if(!promise) {
promise = s.matchProof({proof, document, documentLoader, expansionMap});
promise = s.matchProof({proof, document, documentLoader});
matchingProofs.set(proof, promise);
}
if(await promise) {
Expand Down
34 changes: 16 additions & 18 deletions lib/jsonld-signatures.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright (c) 2010-2018 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2010-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';

Expand Down Expand Up @@ -37,11 +37,7 @@ const VerificationError = require('./VerificationError');
*
* Advanced optional parameters and overrides:
*
* @param {function} [options.expansionMap] - A custom expansion map that is
* passed to the JSON-LD processor; by default a function that will throw
* an error when unmapped properties are detected in the input, use `false`
* to turn this off and allow unmapped properties to be dropped or use a
* custom function.
* @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).
Expand All @@ -51,6 +47,9 @@ const VerificationError = require('./VerificationError');
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.');
}
Expand All @@ -60,8 +59,7 @@ api.sign = async function sign(document, {
suite.ensureSuiteContext({document, addSuiteContext});

try {
return await new ProofSet().add(
document, {suite, purpose, documentLoader, expansionMap});
return await new ProofSet().add(document, {suite, purpose, documentLoader});
} catch(e) {
if(!documentLoader && e.name === 'jsonld.InvalidUrl') {
const {details: {url}} = e;
Expand All @@ -81,7 +79,8 @@ api.sign = async function sign(document, {
* @param {object} document - The JSON-LD document with one or more proofs to be
* verified.
*
* @param {LinkedDataSignature|LinkedDataSignature[]} suite -
* @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
Expand All @@ -90,13 +89,9 @@ api.sign = async function sign(document, {
*
* Advanced optional parameters and overrides:
*
* @param {function} [documentLoader] - A custom document loader,
* @param {function} [options.documentLoader] - A custom document loader,
* `Promise<RemoteDocument> documentLoader(url)`.
* @param {function} [expansionMap] - A custom expansion map that is
* passed to the JSON-LD processor; by default a function that will throw
* an error when unmapped properties are detected in the input, use `false`
* to turn this off and allow unmapped properties to be dropped or use a
* custom function.
* @param {function} [options.expansionMap] - NOT SUPPORTED; do not use.
*
* @return {Promise<{verified: boolean, results: Array,
* error: VerificationError}>}
Expand All @@ -107,12 +102,16 @@ api.sign = async function sign(document, {
* containing all of the errors that occurred during the verification process.
*/
api.verify = async function verify(document, {
suite, purpose, documentLoader, expansionMap} = {}) {
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, expansionMap});
document, {suite, purpose, documentLoader});
const {error} = result;
if(error) {
if(!documentLoader && error.name === 'jsonld.InvalidUrl') {
Expand All @@ -136,4 +135,3 @@ api.purposes = require('./purposes').purposes;

// expose document loader helpers
Object.assign(api, require('./documentLoader'));

4 changes: 2 additions & 2 deletions lib/purposes/ControllerProofPurpose.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright (c) 2018 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';

Expand Down Expand Up @@ -108,7 +108,7 @@ module.exports = class ControllerProofPurpose extends ProofPurpose {
'@embed': '@never',
id: verificationId
}
}, {documentLoader, compactToRelative: false});
}, {documentLoader, compactToRelative: false, safe: true});
}
result.controller = document;
}
Expand Down
16 changes: 13 additions & 3 deletions lib/purposes/ProofPurpose.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ module.exports = class ProofPurpose {
*/
async validate(
proof, {/*document, suite, verificationMethod,
documentLoader, expansionMap*/}) {
documentLoader,*/ expansionMap}) {
if(expansionMap) {
throw new Error('"expansionMap" not supported.');
}

try {
// check expiration
if(this.maxTimestampDelta !== Infinity) {
Expand Down Expand Up @@ -73,7 +77,10 @@ module.exports = class ProofPurpose {
* @return {Promise<object>} resolves to the proof instance (in the
* `constants.SECURITY_CONTEXT_URL`.
*/
async update(proof, {/*document, suite, documentLoader, expansionMap */}) {
async update(proof, {/*document, suite, documentLoader,*/ expansionMap}) {
if(expansionMap) {
throw new Error('"expansionMap" not supported.');
}
proof.proofPurpose = this.term;
return proof;
}
Expand All @@ -87,7 +94,10 @@ module.exports = class ProofPurpose {
*
* @return {Promise<boolean>} `true` if there's a match, `false` if not.
*/
async match(proof, {/* document, documentLoader, expansionMap */}) {
async match(proof, {/* document, documentLoader,*/ expansionMap}) {
if(expansionMap) {
throw new Error('"expansionMap" not supported.');
}
return proof.proofPurpose === this.term;
}
};
26 changes: 14 additions & 12 deletions lib/suites/LinkedDataProof.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright (c) 2018 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';

Expand All @@ -12,10 +12,11 @@ module.exports = class LinkedDataProof {
}

/**
* @param document {object} to be signed.
* @param purpose {ProofPurpose}
* @param documentLoader {function}
* @param expansionMap {function}
* @param {object} options - The options to use.
* @param {object} options.document - The document to be signed.
* @param {ProofPurpose} options.purpose - The proof purpose instance.
* @param {function} options.documentLoader - The document loader to use.
* @param {function} options.expansionMap - NOT SUPPORTED; do not use.
*
* @returns {Promise<object>} Resolves with the created proof object.
*/
Expand All @@ -26,11 +27,12 @@ module.exports = class LinkedDataProof {
}

/**
* @param proof {object} the proof to be verified.
* @param document {object} the document the proof applies to.
* @param purpose {ProofPurpose}
* @param documentLoader {function}
* @param expansionMap {function}
* @param {object} options - The options to use.
* @param {object} options.proof - The proof to be verified.
* @param {object} options.document - The document the proof applies to.
* @param {ProofPurpose} options.purpose - The proof purpose instance.
* @param {function} options.documentLoader - The document loader to use.
* @param {function} options.expansionMap - NOT SUPPORTED; do not use.
*
* @returns {Promise<{object}>} Resolves with the verification result.
*/
Expand All @@ -43,8 +45,8 @@ module.exports = class LinkedDataProof {
/**
* Checks whether a given proof exists in the document.
*
* @param {object} options - Options hashmap.
* @param {object} options.proof
* @param {object} options - The options to use.
* @param {object} options.proof - The proof to match.
*
* @returns {Promise<boolean>} Whether a match for the proof was found.
*/
Expand Down
Loading

0 comments on commit d17c390

Please sign in to comment.