<commit_before>/*
 * Copyright (c) 2017-2018 Digital Bazaar, Inc. All rights reserved.
 */
'use strict';

const constants = require('../constants');
const util = require('../util');
const Helper = require('../Helper');

// TODO: reorganize this class further and make it more obvious which
// methods need to be extended in proof plugins

// TODO: make signature and verification code (and potentially other code)
// more DRY, especially wrt. plugins having reimplement functionality

module.exports = class LinkedDataSignature {
  constructor(injector, algorithm) {
    this.injector = injector;
    this.algorithm = algorithm;
    this.helper = new Helper(injector);
  }

  async canonize(input, options) {
    const jsonld = this.injector.use('jsonld');
    const opts = {
      algorithm: 'URDNA2015',
      format: 'application/n-quads',
      expansionMap: options.expansionMap
    };
    if(options.documentLoader) {
      opts.documentLoader = options.documentLoader;
    }
    return jsonld.canonize(input, opts);
  }

  async createVerifyData(input, options) {
    // TODO: frame before getting signature, not just compact? considerations:
    // should the assumption be (for this library) that the signature is on
    // the top-level object and thus framing is unnecessary?

    const jsonld = this.injector.use('jsonld');
    const opts = {expansionMap: options.expansionMap};
    if(options.documentLoader) {
      opts.documentLoader = options.documentLoader;
    }
    const compacted = await jsonld.compact(
      input, constants.SECURITY_CONTEXT_URL, opts);

    // TODO: will need to preserve `proof` when chained signature
    // option is used and implemented in the future

    // delete the existing proofs(s) prior to canonicalization
    delete compacted.proof;

    // ensure signature values are removed from proof node
    const proof = await this.sanitizeProofNode(options.proof, options);

    // concatenate hash of c14n proof options and hash of c14n document
    const c14nProofOptions = await this.canonize(proof, options);
    const c14nDocument = await this.canonize(compacted, options);
    return {
      data: this._sha256(c14nProofOptions).getBytes() +
        this._sha256(c14nDocument).getBytes(),
      encoding: 'binary'
    };
  }

  async sanitizeProofNode(proof, options) {
    // `jws`,`signatureValue`,`proofValue` must not be included in the proof
    // options
    proof = util.deepClone(options.proof);
    delete proof.jws;
    delete proof.signatureValue;
    delete proof.proofValue;
    return proof;
  }

  async sign(input, options) {
    // copy options for setting defaults
    options = Object.assign({}, options || {});

    // validate common options
    if('creator' in options && typeof options.creator !== 'string') {
      throw new TypeError('"options.creator" must be a URL string.');
    }
    if('domain' in options && typeof options.domain !== 'string') {
      throw new TypeError('"options.domain" must be a string.');
    }
    if('nonce' in options && typeof options.nonce !== 'string') {
      throw new TypeError('"options.nonce" must be a string.');
    }

    // disallow dropping properties when expanding by default
    if(options.expansionMap !== false) {
      options.expansionMap = info => {
        if(info.unmappedProperty) {
          throw new Error('The property "' +
             info.unmappedProperty + '" in the input ' +
            'was not defined in the context.');
        }
      };
    }<commit_msg>Pass `proof` instead of `options.proof`.<commit_after>/*
 * Copyright (c) 2017-2018 Digital Bazaar, Inc. All rights reserved.
 */
'use strict';

const constants = require('../constants');
const util = require('../util');
const Helper = require('../Helper');

// TODO: reorganize this class further and make it more obvious which
// methods need to be extended in proof plugins

// TODO: make signature and verification code (and potentially other code)
// more DRY, especially wrt. plugins having reimplement functionality

module.exports = class LinkedDataSignature {
  constructor(injector, algorithm) {
    this.injector = injector;
    this.algorithm = algorithm;
    this.helper = new Helper(injector);
  }

  async canonize(input, options) {
    const jsonld = this.injector.use('jsonld');
    const opts = {
      algorithm: 'URDNA2015',
      format: 'application/n-quads',
      expansionMap: options.expansionMap
    };
    if(options.documentLoader) {
      opts.documentLoader = options.documentLoader;
    }
    return jsonld.canonize(input, opts);
  }

  async createVerifyData(input, options) {
    // TODO: frame before getting signature, not just compact? considerations:
    // should the assumption be (for this library) that the signature is on
    // the top-level object and thus framing is unnecessary?

    const jsonld = this.injector.use('jsonld');
    const opts = {expansionMap: options.expansionMap};
    if(options.documentLoader) {
      opts.documentLoader = options.documentLoader;
    }
    const compacted = await jsonld.compact(
      input, constants.SECURITY_CONTEXT_URL, opts);

    // TODO: will need to preserve `proof` when chained signature
    // option is used and implemented in the future

    // delete the existing proofs(s) prior to canonicalization
    delete compacted.proof;

    // ensure signature values are removed from proof node
    const proof = await this.sanitizeProofNode(options.proof, options);

    // concatenate hash of c14n proof options and hash of c14n document
    const c14nProofOptions = await this.canonize(proof, options);
    const c14nDocument = await this.canonize(compacted, options);
    return {
      data: this._sha256(c14nProofOptions).getBytes() +
        this._sha256(c14nDocument).getBytes(),
      encoding: 'binary'
    };
  }

  async sanitizeProofNode(proof, options) {
    // `jws`,`signatureValue`,`proofValue` must not be included in the proof
    // options
    proof = util.deepClone(proof);
    delete proof.jws;
    delete proof.signatureValue;
    delete proof.proofValue;
    return proof;
  }

  async sign(input, options) {
    // copy options for setting defaults
    options = Object.assign({}, options || {});

    // validate common options
    if('creator' in options && typeof options.creator !== 'string') {
      throw new TypeError('"options.creator" must be a URL string.');
    }
    if('domain' in options && typeof options.domain !== 'string') {
      throw new TypeError('"options.domain" must be a string.');
    }
    if('nonce' in options && typeof options.nonce !== 'string') {
      throw new TypeError('"options.nonce" must be a string.');
    }

    // disallow dropping properties when expanding by default
    if(options.expansionMap !== false) {
      options.expansionMap = info => {
        if(info.unmappedProperty) {
          throw new Error('The property "' +
             info.unmappedProperty + '" in the input ' +
            'was not defined in the context.');
        }
      };
    }
{'fraction_non_alphanumeric': 0.1100355871886121, 'fraction_numerical': 0.008540925266903915, 'mean_word_length': 3.1087719298245613, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2249431', 'n_tokens_mistral': 1991, 'n_tokens_neox': 1929, 'n_words': 618}