Source: utils/vc/helpers.js

import jsonld from 'jsonld';
import { JsonWebKey } from '@transmute/json-web-signature';
import defaultDocumentLoader from './document-loader';

import {
  EcdsaSecp256k1VerKeyName,
  Ed25519VerKeyName,
  Sr25519VerKeyName,
  EcdsaSecp256k1Signature2019,
  Ed25519Signature2018,
  Sr25519Signature2020,
  Bls12381BBSSignatureDock2022,
  Bls12381BBSDockVerKeyName,
  Bls12381BBS23DockVerKeyName,
  Bls12381BBSSignatureDock2023,
  Bls12381PSSignatureDock2023,
  Bls12381PSDockVerKeyName,
  JsonWebSignature2020,
  Ed25519Signature2020,
} from './custom_crypto';
import { Bls12381BDDT16DockVerKeyName, Bls12381BDDT16MacDockName, Ed255192020VerKeyName } from './crypto/constants';
import Bls12381BDDT16MACDock2024 from './crypto/Bls12381BDDT16MACDock2024';

/**
 * @typedef {object} KeyDoc The Options to use in the function createUser.
 * @property {string} id The key's ID
 * @property {any} controller The key's controller ste
 * @property {any} type the type of key, Sr25519VerificationKey2020 or Ed25519VerificationKey2018 or EcdsaSecp256k1VerificationKey2019
 * @property {object} keypair Keypair is generated by either using polkadot-js's keyring or utils
 * @property {object} publicKey The key's public key taken from the keypair
 */

/**
 * Helper to get the key doc in a format needed for vc.js.
 * @param {string} did - DID in fully qualified form
 * @param {object} keypair - Keypair is generated by either using polkadot-js's keyring for Sr25519 and
 * Ed25519 or keypair generated with `generateEcdsaSecp256k1Keypair` for curve secp256k1.
 * @param {string} type - the type of key, Sr25519VerificationKey2020 or Ed25519VerificationKey2018 or EcdsaSecp256k1VerificationKey2019 or Bls12381G2VerificationKeyDock2022
 * @param {string} id - the ID of the key for future resolution
 * @returns {KeyDoc}
 */
export function getKeyDoc(did, keypair, type, id) {
  return {
    id: id || `${did}#keys-1`,
    controller: did,
    type: type || keypair.verKeyType,
    keypair: keypair.keyPair || keypair,
  };
}

/**
 * Get signature suite from a keyDoc
 * @param {object} keyDoc - key document containing `id`, `controller`, `type`, `privateKeyBase58` and `publicKeyBase58`
 * @returns {object} - signature suite.
 */
export async function getSuiteFromKeyDoc(keyDoc, useProofValue, options) {
  // Check if passing suite directly
  if (keyDoc.verificationMethod) {
    return keyDoc;
  }

  let Cls;
  let { keypair } = keyDoc;
  switch (keyDoc.type) {
    case EcdsaSecp256k1VerKeyName:
      Cls = EcdsaSecp256k1Signature2019;
      break;
    case Ed25519VerKeyName:
      Cls = Ed25519Signature2018;
      break;
    case Ed255192020VerKeyName:
      Cls = Ed25519Signature2020;
      break;
    case Sr25519VerKeyName:
      Cls = Sr25519Signature2020;
      break;
    case Bls12381BBSDockVerKeyName:
      Cls = Bls12381BBSSignatureDock2022;
      break;
    case Bls12381BBS23DockVerKeyName:
      Cls = Bls12381BBSSignatureDock2023;
      break;
    case Bls12381PSDockVerKeyName:
      Cls = Bls12381PSSignatureDock2023;
      break;
    case Bls12381BDDT16DockVerKeyName:
      Cls = Bls12381BDDT16MACDock2024;
      break;
    case 'JsonWebKey2020':
      Cls = JsonWebSignature2020;
      if (!keypair) {
        keypair = await JsonWebKey.from(keyDoc, options);
      }
      break;
    default:
      throw new Error(`Unknown key type ${keyDoc.type}.`);
  }

  return new Cls({
    ...keyDoc,
    verificationMethod: keyDoc.id,
    useProofValue,
    keyDoc,
    keypair,
  });
}

/**
 * Helper method to ensure credential is valid according to the context
 * @param credential
 */
export async function expandJSONLD(credential, options = {}) {
  if (options.documentLoader && options.resolver) {
    throw new Error(
      'Passing resolver and documentLoader results in resolver being ignored, please re-factor.',
    );
  }

  const expanded = await jsonld.expand(credential, {
    ...options,
    documentLoader:
      options.documentLoader || defaultDocumentLoader(options.resolver),
  });
  return expanded[0];
}

export function potentialToArray(a) {
  /* eslint-disable no-nested-ternary */
  return a ? (Array.isArray(a) ? a : [a]) : [];
}

export function getKeyFromDIDDocument(didDocument, didUrl) {
  // Ensure not already a key doc
  if (
    didDocument.publicKeyBase58
    || didDocument.publicKeyMultibase
    || didDocument.publicKeyJwk
    || (didDocument.publicKey && !Array.isArray(didDocument.publicKey))
  ) {
    return didDocument;
  }

  const possibleKeys = [
    ...potentialToArray(didDocument.verificationMethod),
    ...potentialToArray(didDocument.keyAgreement),
    ...potentialToArray(didDocument.publicKey),
  ];
  return possibleKeys.filter((key) => key.id === didUrl)[0];
}

/**
 * For KVAC, public key is not present so the holder assumes that the given credential is valid.
 * Secondly, these credentials are never shared as it is with the verifier so this function returns true if the credential is
 * a KVAC, else returns undefined.
 * @param credential
 * @returns {object | undefined}
 */
export function processIfKvac(credential) {
  const { proof } = credential;
  if (proof === undefined || proof.type === undefined) {
    throw new Error(`Credential should have a non-null type field but found ${proof.type}`);
  }
  if (proof.type === Bls12381BDDT16MacDockName) {
    return {
      results: [{
        verified: true, proof, verificationMethod: {}, purposeResult: {},
      }],
      verified: true,
    };
  }
  return undefined;
}