'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var jsigs = require('jsonld-signatures');
var b58 = require('bs58');
var base64url = require('base64url');
var jsonld = require('jsonld');
var types_did_document_verificationMethodRef = require('../../../types/did/document/verification-method-ref.cjs');
var vc_jws = require('../../jws.cjs');
require('../../../types/generic/typed-enum.cjs');
require('../../../utils/types/array.cjs');
require('../../../utils/inheritance.cjs');
require('../../../utils/types/object.cjs');
require('../../../utils/types/iterable.cjs');
require('../../../utils/types/set.cjs');
require('crypto');
require('../../../utils/interfaces.cjs');
require('../../../utils/assert.cjs');
require('../../../utils/error.cjs');
require('multiformats/bases/base58');
require('varint');
require('../../../utils/encoding/ss-58.cjs');
require('blake2b');
require('../../../utils/types/bytes.cjs');
require('../../../utils/types/string.cjs');
require('../../../types/generic/with-base.cjs');
require('../../../types/generic/with-catch-null.cjs');
require('../../../types/generic/with-eq.cjs');
require('../../../types/generic/any-of.cjs');
require('../../../types/generic/with-from.cjs');
require('../../../types/generic/with-from-json-bytes.cjs');
require('../../../types/generic/typed-null.cjs');
require('../../../types/generic/create-placeholder.cjs');
require('../../../types/generic/typed-bytes.cjs');
require('../../../types/generic/typed-string.cjs');
require('../../../types/generic/typed-array.cjs');
require('../../../types/generic/typed-tuple.cjs');
require('../../../types/generic/typed-number.cjs');
require('../../../types/generic/typed-set.cjs');
require('../../../types/generic/typed-map.cjs');
require('../../../types/generic/typed-struct.cjs');
require('uuid');
require('js-sha256');
require('../../../types/generic/with-qualifier.cjs');
require('../../../types/generic/utils.cjs');
require('../../../types/generic/typed-bytes-array.cjs');
require('../../../types/did/onchain/typed-did/index.cjs');
require('../../../types/did/onchain/typed-did/did-method-key/did-method-key-public-key.cjs');
require('../../../utils/encoding/base-58-btc.cjs');
require('../../../types/generic/sized.cjs');
require('../../../utils/types/number.cjs');
require('../../../types/public-keys/public-key.cjs');
require('../../../types/public-keys/public-key-x25519-value.cjs');
require('../../../types/public-keys/public-key-ed25519-value.cjs');
require('../constants.cjs');
require('../../../types/public-keys/public-key-secp256k1-value.cjs');
require('../../../types/public-keys/public-key-sr25519-value.cjs');
require('../../../types/did/onchain/constants.cjs');
require('../../../types/did/onchain/typed-did/signature.cjs');
require('../../../types/did/onchain/typed-did/did-method-key/did-method-key-signature.cjs');
require('../../../types/signatures/signature-value.cjs');
require('../../../types/signatures/signature-ed25519-value.cjs');
require('../../../types/signatures/signature-secp256k1-value.cjs');
require('../../../types/signatures/signature.cjs');
require('../../../types/signatures/signature-sr25519-value.cjs');
require('../../../types/did/onchain/typed-did/dock-did-value.cjs');
require('../../../types/generic/typed-uuid.cjs');
require('../../../types/did/onchain/typed-did/cheqd-did.cjs');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var jsigs__default = /*#__PURE__*/_interopDefault(jsigs);
var b58__default = /*#__PURE__*/_interopDefault(b58);
var base64url__default = /*#__PURE__*/_interopDefault(base64url);
var jsonld__default = /*#__PURE__*/_interopDefault(jsonld);

const MULTIBASE_BASE58BTC_HEADER = 'z';

function decodeBase64Url(string) {
  const buffer = base64url__default.default.toBuffer(string);
  return new Uint8Array(buffer);
}

function decodeBase64UrlToString(string) {
  return base64url__default.default.decode(string);
}

class CustomLinkedDataSignature extends jsigs__default.default.suites
  .LinkedDataSignature {
  /**
   * Creates a new CustomLinkedDataSignature instance
   * @constructor
   * @param {object} config - Configuration options
   */
  constructor(config) {
    super(config);
    this.alg = config.alg;
    this.useProofValue = config.useProofValue || false;
  }

  /**
   * @param document {object} to be signed.
   * @param proof {object}
   * @param documentLoader {function}
   */
  static async getVerificationMethod({ proof, documentLoader }) {
    let { verificationMethod } = proof;
    if (typeof verificationMethod === 'object') {
      verificationMethod = verificationMethod.id;
    }
    if (!verificationMethod) {
      throw new Error('No "verificationMethod" found in proof.');
    }

    // Note: `expansionMap` is intentionally not passed; we can safely drop
    // properties here and must allow for it
    const result = await jsonld__default.default.frame(
      verificationMethod,
      {
        '@context': jsigs__default.default.SECURITY_CONTEXT_URL,
        '@embed': '@always',
        id: types_did_document_verificationMethodRef.possibleVerificationMethodRefs(verificationMethod),
      },
      {
        documentLoader,
        compactToRelative: false,
        expandContext: jsigs__default.default.SECURITY_CONTEXT_URL,
      },
    );

    if (!result) {
      throw new Error(`Verification method ${verificationMethod} not found.`);
    }
    // ensure verification method has not been revoked
    if (result.revoked !== undefined) {
      throw new Error('The verification method has been revoked.');
    }
    return result;
  }

  /**
   * @param document {object} to be signed.
   * @param proof {object}
   * @param documentLoader {function}
   */
  async getVerificationMethod({ proof, documentLoader }) {
    return this.constructor.getVerificationMethod({
      proof,
      documentLoader,
    });
  }

  /**
   * Verifies the proof signature against the given data.
   *
   * @param {object} options - The options to use.
   * @param {Uint8Array} options.verifyData - Canonicalized hashed data.
   * @param {object} options.verificationMethod - Key object.
   * @param {object} options.proof - The proof to be verified.
   *
   * @returns {Promise<boolean>} Resolves with the verification result.
   */
  async verifySignature({ verifyData, verificationMethod, proof }) {
    let signatureBytes;
    let data = verifyData;

    const { proofValue, jws } = proof;
    if (proofValue && typeof proofValue === 'string') {
      signatureBytes = b58__default.default.decode(
        CustomLinkedDataSignature.fromJsigProofValue(proofValue),
      );
    } else if (jws && typeof jws === 'string') {
      // Fallback to older jsonld-signature implementations
      const [encodedHeader /* payload */, , encodedSignature] = jws.split('.');

      let header;
      try {
        header = JSON.parse(decodeBase64UrlToString(encodedHeader));
      } catch (e) {
        throw new Error(`Could not parse JWS header; ${e}`);
      }
      if (!(header && typeof header === 'object')) {
        throw new Error('Invalid JWS header.');
      }

      signatureBytes = decodeBase64Url(encodedSignature);
      data = vc_jws.createJws({ encodedHeader, verifyData });
    }

    let { verifier } = this;
    if (!verifier) {
      const key = await this.LDKeyClass.from(verificationMethod);
      verifier = key.verifier();
    }
    return verifier.verify({ data, signature: signatureBytes });
  }

  /**
   * Adds a signature (proofValue) field to the proof object. Called by
   * LinkedDataSignature.createProof().
   *
   * @param {object} options - The options to use.
   * @param {Uint8Array} options.verifyData - Data to be signed (extracted
   *   from document, according to the suite's spec).
   * @param {object} options.proof - Proof object (containing the proofPurpose,
   *   verificationMethod, etc).
   *
   * @returns {Promise<object>} Resolves with the proof containing the signature
   *   value.
   */
  async sign({ verifyData, proof }) {
    if (!(this.signer && typeof this.signer.sign === 'function')) {
      throw new Error('A signer API has not been specified.');
    }

    const getSigBytes = async (data) => {
      let signatureBytes;
      const signature = await this.signer.sign({ data });
      if (typeof signature === 'string') {
        // Some signers will return a string like: header..signature
        // split apart those strings to get the signature in bytes
        const signatureSplit = signature.split('.');
        const signatureEncoded = signatureSplit[signatureSplit.length - 1];
        signatureBytes = decodeBase64Url(signatureEncoded);
      } else {
        signatureBytes = signature;
      }
      return signatureBytes;
    };

    const finalProof = { ...proof };
    if (this.useProofValue) {
      const signatureBytes = await getSigBytes(verifyData);
      finalProof.proofValue = CustomLinkedDataSignature.encodeProofValue(signatureBytes);
    } else {
      if (!this.alg) {
        throw new Error("Suite doesn't contain required alg parameter");
      }

      const header = { alg: this.alg, b64: false, crit: ['b64'] };
      const encodedHeader = base64url__default.default.encode(JSON.stringify(header));
      const jwsData = vc_jws.createJws({ encodedHeader, verifyData });
      const signatureBytesJWS = await getSigBytes(jwsData);
      finalProof.jws = `${encodedHeader}..${base64url__default.default.encode(
        signatureBytesJWS,
      )}`;
    }

    return finalProof;
  }

  static encodeProofValue(signatureBytes) {
    return MULTIBASE_BASE58BTC_HEADER + b58__default.default.encode(signatureBytes);
  }

  /**
   * Json-ld signs prefix signatures with a specific character. Removes that character
   * @param proofValue
   * @returns {*|string}
   */
  static fromJsigProofValue(proofValue) {
    if (proofValue[0] !== MULTIBASE_BASE58BTC_HEADER) {
      throw new Error('Only base58btc multibase encoding is supported.');
    }
    return proofValue.substring(1);
  }
}

exports.decodeBase64Url = decodeBase64Url;
exports.decodeBase64UrlToString = decodeBase64UrlToString;
exports.default = CustomLinkedDataSignature;
//# sourceMappingURL=CustomLinkedDataSignature.cjs.map
