PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-core/src/bitgo/tss/ecdsa

Просмотр файла: ecdsa.ts

import { Ecdsa } from './../../../account-lib/mpc/tss';
import {
  AShare,
  BShare,
  CombinedKey,
  CreateUserOmicronAndDeltaShareRT,
  DecryptableNShare,
  DShare,
  EncryptedNShare,
  GShare,
  KeyShare,
  NShare,
  OShare,
  ReceivedShareType,
  SendShareToBitgoRT,
  SendShareType,
  Signature,
  SignatureShare,
  SigningMaterial,
  SignShare,
  WShare,
  XShareWithChallenges,
  YShareWithChallenges,
} from './types';
import { createShareProof, RequestType, SignatureShareRecord, SignatureShareType } from '../../utils';
import { ShareKeyPosition } from '../types';
import { BitGoBase } from '../../bitgoBase';
import {
  KShare,
  MUShare,
  RangeProofShare,
  RangeProofWithCheckShare,
  SignConvertStep2Response,
  SShare,
} from '../../../account-lib/mpc/tss/ecdsa/types';
import { commonVerifyWalletSignature, getTxRequest, sendSignatureShare } from '../common';
import createKeccakHash from 'keccak';
import assert from 'assert';
import { bip32, ecc } from '@bitgo/utxo-lib';
import * as pgp from 'openpgp';
import bs58 from 'bs58';
import { ApiKeyShare } from '../../keychain';
import { Hash } from 'crypto';
import { EcdsaPaillierProof } from '@bitgo/sdk-lib-mpc';
import { IRequestTracer } from '../../../api';

const MPC = new Ecdsa();

/**
 * Combines NShares to combine the final TSS key
 * This can only be used to create the User or Backup key since it requires the common keychain from BitGo first
 *
 * @param keyShare - TSS key share
 * @param encryptedNShares - encrypted NShares with information on how to decrypt
 * @param commonKeychain - expected common keychain of the combined key
 * @returns {CombinedKey} combined TSS key
 */
export async function createCombinedKey(
  keyShare: KeyShare,
  encryptedNShares: DecryptableNShare[],
  commonKeychain: string
): Promise<CombinedKey> {
  const nShares: NShare[] = [];

  let bitgoNShare: NShare | undefined;
  let userNShare: NShare | undefined;
  let backupNShare: NShare | undefined;

  for (const encryptedNShare of encryptedNShares) {
    const nShare = await decryptNShare(encryptedNShare, encryptedNShare.isbs58Encoded);

    switch (encryptedNShare.nShare.j) {
      case 1:
        userNShare = nShare;
        break;
      case 2:
        backupNShare = nShare;
        break;
      case 3:
        bitgoNShare = nShare;
        break;
      default:
        throw new Error('Invalid NShare index');
    }

    nShares.push(nShare);
  }

  if (!bitgoNShare) {
    throw new Error('Missing BitGo N Share');
  }

  const combinedKey = MPC.keyCombine(keyShare.pShare, nShares);
  if (combinedKey.xShare.y + combinedKey.xShare.chaincode !== commonKeychain) {
    throw new Error('Common keychains do not match');
  }

  const signingMaterial: SigningMaterial = {
    pShare: keyShare.pShare,
    bitgoNShare,
    backupNShare,
    userNShare,
  };

  return {
    signingMaterial,
    commonKeychain,
  };
}

/**
 * Creates the SignShare with User XShare and YShare Corresponding to BitGo
 * @param {XShare} xShare User secret xShare
 * @param {YShare} yShare YShare from Bitgo
 * @returns {Promise<SignShare>}
 */
export async function createUserSignShare(
  xShare: XShareWithChallenges,
  yShare: YShareWithChallenges
): Promise<SignShare> {
  if (xShare.i !== ShareKeyPosition.USER) {
    throw new Error(`Invalid XShare, XShare doesn't belong to the User`);
  }

  if (yShare.i !== ShareKeyPosition.USER || yShare.j !== ShareKeyPosition.BITGO) {
    throw new Error('Invalid YShare provided for sign');
  }
  return await MPC.signShare(xShare, yShare);
}

/**
 * Creates the Gamma Share and MuShare with User WShare and AShare From BitGo
 * @param {WShare} wShare User WShare
 * @param {AShare} aShare AShare from Bitgo
 * @returns {Promise<SignConvertStep2Response>}
 */
export async function createUserGammaAndMuShare(wShare: WShare, aShare: AShare): Promise<SignConvertStep2Response> {
  if (wShare.i !== ShareKeyPosition.USER) {
    throw new Error(`Invalid WShare, doesn't belong to the User`);
  }
  if (aShare.i !== ShareKeyPosition.USER || aShare.j !== ShareKeyPosition.BITGO) {
    throw new Error('Invalid AShare, is not from Bitgo to User');
  }
  return MPC.signConvertStep2({ wShare, aShare });
}

/**
 * Creates the Omicron Share and Delta share with user GShare
 * @param {GShare} gShare User GShare
 * @returns {Promise<CreateUserOmicronAndDeltaShareRT>}
 */
export async function createUserOmicronAndDeltaShare(gShare: GShare): Promise<CreateUserOmicronAndDeltaShareRT> {
  if (gShare.i !== ShareKeyPosition.USER) {
    throw new Error(`Invalid GShare, doesn't belong to the User`);
  }
  return MPC.signCombine({
    gShare: gShare,
    signIndex: {
      i: ShareKeyPosition.BITGO,
      j: gShare.i,
    },
  });
}

/**
 * Creates the Signature Share with User OShare and DShare From BitGo
 * @param {OShare} oShare User OShare
 * @param {DShare} dShare DShare from bitgo
 * @param {Buffer} message message to perform sign
 * @returns {Promise<createUserSignShareRT>}
 */
export async function createUserSignatureShare(
  oShare: OShare,
  dShare: DShare,
  message: Buffer,
  hash: Hash = createKeccakHash('keccak256') as Hash
): Promise<SignatureShare> {
  if (oShare.i !== ShareKeyPosition.USER) {
    throw new Error(`Invalid OShare, doesn't belong to the User`);
  }

  if (dShare.i !== ShareKeyPosition.USER || dShare.j !== ShareKeyPosition.BITGO) {
    throw new Error(`Invalid DShare, doesn't seem to be from BitGo`);
  }
  return MPC.sign(message, oShare, dShare, hash);
}

export type MuDShare = { muShare: MUShare; dShare: DShare; i: ShareKeyPosition };

/**
 * Sends Share To Bitgo
 * @param {BitGoBase} bitgo - the bitgo instance
 * @param {String} walletId - the wallet id  *
 * @param {String} txRequestId - the txRequest Id
 * @param requestType - the type of request being submitted (either tx or message for signing)
 * @param shareType
 * @param share
 * @param signerShare
 * @param vssProof - the v value of the share
 * @param privateShareProof - the uSig of the share
 * @param publicShare - the y value of the share
 * @param userPublicGpgKey - the public key of the gpg key used for creating the privateShareProof
 * @param reqId - request tracer request id
 * @returns {Promise<SignatureShareRecord>} - a Signature Share
 */
export async function sendShareToBitgo(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  requestType: RequestType,
  shareType: SendShareType,
  share: SShare | MuDShare | KShare,
  signerShare?: string,
  vssProof?: string,
  privateShareProof?: string,
  publicShare?: string,
  userPublicGpgKey?: string,
  reqId?: IRequestTracer
): Promise<SendShareToBitgoRT> {
  if (shareType !== SendShareType.SShare && share.i !== ShareKeyPosition.BITGO) {
    throw new Error('Invalid Share, is not from User to Bitgo');
  }
  let signatureShare: SignatureShareRecord;
  let responseFromBitgo: SendShareToBitgoRT;

  switch (shareType) {
    case SendShareType.KShare:
      assert(signerShare, `signer share must be present`);
      const kShare = share as KShare;
      signatureShare = convertKShare(kShare);
      signatureShare.vssProof = vssProof;
      signatureShare.publicShare = publicShare;
      signatureShare.privateShareProof = privateShareProof;
      await sendSignatureShare(
        bitgo,
        walletId,
        txRequestId,
        signatureShare,
        requestType,
        signerShare,
        'ecdsa',
        'full',
        userPublicGpgKey,
        reqId
      );
      responseFromBitgo = await getBitgoToUserLatestShare(
        bitgo,
        walletId,
        txRequestId,
        ReceivedShareType.AShare,
        requestType,
        reqId
      );
      break;
    case SendShareType.MUShare:
      const shareToSend = share as MuDShare;
      const muShareRecord = convertMuShare(shareToSend.muShare);
      const dShareRecord = convertDShare(shareToSend.dShare);
      signatureShare = {
        to: SignatureShareType.BITGO,
        from: getParticipantFromIndex(shareToSend.dShare.j),
        share: `${muShareRecord.share}${secondaryDelimeter}${dShareRecord.share}`,
      };
      await sendSignatureShare(
        bitgo,
        walletId,
        txRequestId,
        signatureShare,
        requestType,
        signerShare,
        'ecdsa',
        undefined,
        undefined,
        reqId
      );
      responseFromBitgo = await getBitgoToUserLatestShare(
        bitgo,
        walletId,
        txRequestId,
        ReceivedShareType.DShare,
        requestType,
        reqId
      );
      break;
    case SendShareType.SShare:
      const sShare = share as SShare;
      signatureShare = convertSignatureShare(sShare, 1, 3);
      await sendSignatureShare(
        bitgo,
        walletId,
        txRequestId,
        signatureShare,
        requestType,
        signerShare,
        'ecdsa',
        undefined,
        undefined,
        reqId
      );
      responseFromBitgo = sShare;
      break;
    default:
      throw new Error('Invalid Share given to send');
  }

  return responseFromBitgo;
}

/**
 * Gets the latest user's share from bitgo needed to continue signing flow
 * @param {BitGoBase} bitgo - the bitgo instance
 * @param {String} walletId - the wallet id  *
 * @param {String} txRequestId - the txRequest Id
 * @param {ReceivedShareType} shareType - the excpected share type
 * @param {IRequestTracer} reqId - request tracer request id
 * @returns {Promise<SendShareToBitgoRT>} - share from bitgo to user
 */
export async function getBitgoToUserLatestShare(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  shareType: ReceivedShareType,
  requestType: RequestType,
  reqId?: IRequestTracer
): Promise<SendShareToBitgoRT> {
  let responseFromBitgo: SendShareToBitgoRT;
  const txRequest = await getTxRequest(bitgo, walletId, txRequestId, reqId);
  let userShares;
  switch (requestType) {
    case RequestType.tx:
      assert(txRequest.transactions, 'transactions required as part of txRequest');
      userShares = txRequest.transactions[0].signatureShares;
      break;
    case RequestType.message:
      assert(txRequest.messages, 'messages required as part of txRequest');
      userShares = txRequest.messages[0].signatureShares;
      break;
  }
  if (!userShares || !userShares.length) {
    throw new Error('user share is not present');
  }

  const shareRecord = userShares[userShares.length - 1];
  switch (shareType) {
    case ReceivedShareType.AShare:
      responseFromBitgo = parseAShare(shareRecord);
      break;
    case ReceivedShareType.DShare:
      responseFromBitgo = parseDShare(shareRecord);
      break;
    case ReceivedShareType.Signature:
      responseFromBitgo = parseSignatureShare(shareRecord);
      break;
    default:
      throw new Error('Invalid share received');
  }

  return responseFromBitgo;
}

/**
 * Prepares a NShare to be exchanged with other key holders.
 * Output is in a format that is usable within BitGo's ecosystem.
 *
 * @param keyShare - TSS key share of the party preparing exchange materials
 * @param recipientIndex - index of the recipient (1, 2, or 3)
 * @param recipientGpgPublicArmor - recipient's public gpg key in armor format
 * @param senderGpgKey - ephemeral GPG key to encrypt / decrypt sensitve data exchanged between user and server
 * @param isbs58Encoded - is bs58 encoded or not
 * @returns encrypted N Share
 */
export async function encryptNShare(
  keyShare: KeyShare,
  recipientIndex: number,
  recipientGpgPublicArmor: string,
  senderGpgKey: pgp.SerializedKeyPair<string>,
  isbs58Encoded = true
): Promise<EncryptedNShare> {
  const nShare = keyShare.nShares[recipientIndex];
  if (!nShare) {
    throw new Error('Invalid recipient');
  }

  const publicShare = Buffer.concat([
    Buffer.from(keyShare.pShare.y, 'hex'),
    Buffer.from(keyShare.pShare.chaincode, 'hex'),
  ]).toString('hex');

  let privateShare;
  if (isbs58Encoded) {
    privateShare = bip32.fromPrivateKey(Buffer.from(nShare.u, 'hex'), Buffer.from(nShare.chaincode, 'hex')).toBase58();
  } else {
    privateShare = Buffer.concat([Buffer.from(nShare.u, 'hex'), Buffer.from(nShare.chaincode, 'hex')]).toString('hex');
  }
  const recipientPublicKey = await pgp.readKey({ armoredKey: recipientGpgPublicArmor });

  const encryptedPrivateShare = (await pgp.encrypt({
    message: await pgp.createMessage({
      text: privateShare,
    }),
    encryptionKeys: [recipientPublicKey],
  })) as string;

  return {
    i: nShare.i,
    j: nShare.j,
    publicShare,
    encryptedPrivateShare,
    n: nShare.n,
    vssProof: nShare.v,
    privateShareProof: await createShareProof(senderGpgKey.privateKey, nShare.u, 'ecdsa'),
  };
}

/**
 * Prepares a NShare to be exchanged with other key holders.
 * An API key share received from a third party should already be encrypted
 *
 * @param keyShare - TSS key share of the party preparing exchange materials
 * @returns encrypted N Share
 */
export async function buildNShareFromAPIKeyShare(keyShare: ApiKeyShare): Promise<EncryptedNShare> {
  return {
    i: getParticipantIndex(keyShare.to),
    j: getParticipantIndex(keyShare.from),
    publicShare: keyShare.publicShare,
    encryptedPrivateShare: keyShare.privateShare,
    n: keyShare.n ?? '', // this is not currently needed for key creation
    privateShareProof: keyShare.privateShareProof,
    vssProof: keyShare.vssProof,
  };
}

/**
 * Decrypts encrypted n share
 * @param encryptedNShare - decryptable n share with recipient private gpg key armor and sender public gpg key
 * @param isbs58Encoded
 * @returns N share
 */
export async function decryptNShare(encryptedNShare: DecryptableNShare, isbs58Encoded = true): Promise<NShare> {
  const recipientPrivateKey = await pgp.readKey({ armoredKey: encryptedNShare.recipientPrivateArmor });
  const prv = (
    await pgp.decrypt({
      message: await pgp.readMessage({ armoredMessage: encryptedNShare.nShare.encryptedPrivateShare }),
      decryptionKeys: [recipientPrivateKey as pgp.PrivateKey],
    })
  ).data as string;

  let u: string;
  if (isbs58Encoded) {
    const privateShare = bs58.decode(prv).toString('hex');
    u = privateShare.slice(92, 156);
  } else {
    u = prv.slice(0, 64);
  }

  return {
    i: encryptedNShare.nShare.i,
    j: encryptedNShare.nShare.j,
    n: encryptedNShare.nShare.n,
    y: encryptedNShare.nShare.publicShare.slice(0, 66),
    u: u,
    chaincode: encryptedNShare.nShare.publicShare.slice(66, 130),
    v: encryptedNShare.nShare.vssProof,
  };
}

/**
 * Gets public key from common keychain
 * @param commonKeyChain - common keychain of ecdsa tss
 * @returns public key
 */
export function getPublicKey(commonKeyChain: string): string {
  return commonKeyChain.slice(0, 66);
}

export const delimeter = ':';
export const secondaryDelimeter = '-';

function validateSharesLength(shares: string[], expectedLength: number, shareName: string) {
  if (shares.length < expectedLength) {
    throw new Error(`Invalid ${shareName} share`);
  }
}

function validateOptionalValues(shares: string[], start: number, end: number, shareName: string, valueName: string) {
  let found = false;
  for (let i = start; i < end; i++) {
    if (shares[i]) {
      found = true;
    } else if (found) {
      throw new Error(`Inconsistent optional ${valueName} value in ${shareName} share`);
    }
  }
  return found;
}

/**
 * parses K share from signature share record
 * @param share - signature share record
 * @returns K Share
 */
export function parseKShare(share: SignatureShareRecord): KShare {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 11 + 2 * EcdsaPaillierProof.m, 'K');
  const hasProof = validateOptionalValues(shares, 5, 11, 'K', 'proof');

  const proof: RangeProofShare | undefined = hasProof
    ? {
        z: shares[5],
        u: shares[6],
        w: shares[7],
        s: shares[8],
        s1: shares[9],
        s2: shares[10],
      }
    : undefined;

  return {
    i: getParticipantIndex(share.to),
    j: getParticipantIndex(share.from),
    k: shares[0],
    n: shares[1],
    ntilde: shares[2],
    h1: shares[3],
    h2: shares[4],
    proof,
    p: shares.slice(11, 11 + EcdsaPaillierProof.m),
    sigma: shares.slice(11 + EcdsaPaillierProof.m, 11 + 2 * EcdsaPaillierProof.m),
  };
}

/**
 * convert K share to signature share record
 * @param share - K share
 * @returns signature share record
 */
export function convertKShare(share: KShare): SignatureShareRecord {
  return {
    to: getParticipantFromIndex(share.i),
    from: getParticipantFromIndex(share.j),
    share: `${share.k}${delimeter}${share.n}${delimeter}${share.ntilde}${delimeter}${share.h1}${delimeter}${
      share.h2
    }${delimeter}${share.proof?.z || ''}${delimeter}${share.proof?.u || ''}${delimeter}${
      share.proof?.w || ''
    }${delimeter}${share.proof?.s || ''}${delimeter}${share.proof?.s1 || ''}${delimeter}${
      share.proof?.s2 || ''
    }${delimeter}${(share.p || []).join(delimeter)}${delimeter}${(share.sigma || []).join(delimeter)}`,
  };
}

/**
 * parses A share from signature share record
 * @param share - signature share record
 * @returns A Share
 */
export function parseAShare(share: SignatureShareRecord): AShare {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 37 + EcdsaPaillierProof.m, 'A');
  const hasProof = validateOptionalValues(shares, 7, 13, 'A', 'proof');
  const hasGammaProof = validateOptionalValues(shares, 13, 25, 'A', 'gammaProof');
  const hasWProof = validateOptionalValues(shares, 25, 37, 'A', 'wProof');

  const proof: RangeProofShare | undefined = hasProof
    ? {
        z: shares[7],
        u: shares[8],
        w: shares[9],
        s: shares[10],
        s1: shares[11],
        s2: shares[12],
      }
    : undefined;

  const gammaProof: RangeProofWithCheckShare | undefined = hasGammaProof
    ? {
        z: shares[13],
        zprm: shares[14],
        t: shares[15],
        v: shares[16],
        w: shares[17],
        s: shares[18],
        s1: shares[19],
        s2: shares[20],
        t1: shares[21],
        t2: shares[22],
        u: shares[23],
        x: shares[24],
      }
    : undefined;

  const wProof: RangeProofWithCheckShare | undefined = hasWProof
    ? {
        z: shares[25],
        zprm: shares[26],
        t: shares[27],
        v: shares[28],
        w: shares[29],
        s: shares[30],
        s1: shares[31],
        s2: shares[32],
        t1: shares[33],
        t2: shares[34],
        u: shares[35],
        x: shares[36],
      }
    : undefined;

  return {
    i: getParticipantIndex(share.to),
    j: getParticipantIndex(share.from),
    k: shares[0],
    alpha: shares[1],
    mu: shares[2],
    n: shares[3],
    ntilde: shares[4],
    h1: shares[5],
    h2: shares[6],
    proof,
    gammaProof,
    wProof,
    sigma: shares.slice(37),
  };
}

/**
 * convert A share to signature share record
 * @param share - A share
 * @returns signature share record
 */
export function convertAShare(share: AShare): SignatureShareRecord {
  return {
    to: getParticipantFromIndex(share.i),
    from: getParticipantFromIndex(share.j),
    share: `${share.k}${delimeter}${share.alpha}${delimeter}${share.mu}${delimeter}${share.n}${delimeter}${
      share.ntilde
    }${delimeter}${share.h1}${delimeter}${share.h2}${delimeter}${share.proof?.z || ''}${delimeter}${
      share.proof?.u || ''
    }${delimeter}${share.proof?.w || ''}${delimeter}${share.proof?.s || ''}${delimeter}${
      share.proof?.s1 || ''
    }${delimeter}${share.proof?.s2 || ''}${delimeter}${share.gammaProof?.z || ''}${delimeter}${
      share.gammaProof?.zprm || ''
    }${delimeter}${share.gammaProof?.t || ''}${delimeter}${share.gammaProof?.v || ''}${delimeter}${
      share.gammaProof?.w || ''
    }${delimeter}${share.gammaProof?.s || ''}${delimeter}${share.gammaProof?.s1 || ''}${delimeter}${
      share.gammaProof?.s2 || ''
    }${delimeter}${share.gammaProof?.t1 || ''}${delimeter}${share.gammaProof?.t2 || ''}${delimeter}${
      share.gammaProof?.u || ''
    }${delimeter}${share.gammaProof?.x || ''}${delimeter}${share.wProof?.z || ''}${delimeter}${
      share.wProof?.zprm || ''
    }${delimeter}${share.wProof?.t || ''}${delimeter}${share.wProof?.v || ''}${delimeter}${
      share.wProof?.w || ''
    }${delimeter}${share.wProof?.s || ''}${delimeter}${share.wProof?.s1 || ''}${delimeter}${
      share.wProof?.s2 || ''
    }${delimeter}${share.wProof?.t1 || ''}${delimeter}${share.wProof?.t2 || ''}${delimeter}${
      share.wProof?.u || ''
    }${delimeter}${share.wProof?.x || ''}${delimeter}${(share.sigma || []).join(delimeter)}`,
  };
}

/**
 * parses Mu share from signature share record
 * @param share - signature share record
 * @returns Mu Share
 */
export function parseMuShare(share: SignatureShareRecord): MUShare {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 26, 'Mu');
  const hasGammaProof = validateOptionalValues(shares, 2, 14, 'Mu', 'gammaProof');
  const hasWProof = validateOptionalValues(shares, 14, 26, 'Mu', 'wProof');

  let gammaProof;
  if (hasGammaProof) {
    gammaProof = {
      z: shares[2],
      zprm: shares[3],
      t: shares[4],
      v: shares[5],
      w: shares[6],
      s: shares[7],
      s1: shares[8],
      s2: shares[9],
      t1: shares[10],
      t2: shares[11],
      u: shares[12],
      x: shares[13],
    };
  }

  let wProof;
  if (hasWProof) {
    wProof = {
      z: shares[14],
      zprm: shares[15],
      t: shares[16],
      v: shares[17],
      w: shares[18],
      s: shares[19],
      s1: shares[20],
      s2: shares[21],
      t1: shares[22],
      t2: shares[23],
      u: shares[24],
      x: shares[25],
    };
  }

  return {
    i: getParticipantIndex(share.to),
    j: getParticipantIndex(share.from),
    alpha: shares[0],
    mu: shares[1],
    gammaProof,
    wProof,
  };
}

/**
 * convert Mu share to signature share record
 * @param share - Mu share
 * @returns signature share record
 */
export function convertMuShare(share: MUShare): SignatureShareRecord {
  return {
    to: getParticipantFromIndex(share.i),
    from: getParticipantFromIndex(share.j),
    share: `${share.alpha}${delimeter}${share.mu}${delimeter}${share.gammaProof?.z || ''}${delimeter}${
      share.gammaProof?.zprm || ''
    }${delimeter}${share.gammaProof?.t || ''}${delimeter}${share.gammaProof?.v || ''}${delimeter}${
      share.gammaProof?.w || ''
    }${delimeter}${share.gammaProof?.s || ''}${delimeter}${share.gammaProof?.s1 || ''}${delimeter}${
      share.gammaProof?.s2 || ''
    }${delimeter}${share.gammaProof?.t1 || ''}${delimeter}${share.gammaProof?.t2 || ''}${delimeter}${
      share.gammaProof?.u || ''
    }${delimeter}${share.gammaProof?.x || ''}${delimeter}${share.wProof?.z || ''}${delimeter}${
      share.wProof?.zprm || ''
    }${delimeter}${share.wProof?.t || ''}${delimeter}${share.wProof?.v || ''}${delimeter}${
      share.wProof?.w || ''
    }${delimeter}${share.wProof?.s || ''}${delimeter}${share.wProof?.s1 || ''}${delimeter}${
      share.wProof?.s2 || ''
    }${delimeter}${share.wProof?.t1 || ''}${delimeter}${share.wProof?.t2 || ''}${delimeter}${
      share.wProof?.u || ''
    }${delimeter}${share.wProof?.x || ''}`,
  };
}

/**
 * parses D share from signature share record
 * @param share - signature share record
 * @returns D Share
 */
export function parseDShare(share: SignatureShareRecord): DShare {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 2, 'D');

  return {
    i: getParticipantIndex(share.to),
    j: getParticipantIndex(share.from),
    delta: shares[0],
    Gamma: shares[1],
  };
}

/**
 * convert D share to signature share record
 * @param share - D share
 * @returns signature share record
 */
export function convertDShare(share: DShare): SignatureShareRecord {
  return {
    to: getParticipantFromIndex(share.i),
    from: getParticipantFromIndex(share.j),
    share: `${share.delta}${delimeter}${share.Gamma}`,
  };
}

/**
 * parses S and D share from signature share record
 * @param share - signature share record
 * @returns Object containing S and D Share
 */
export function parseSDShare(share: SignatureShareRecord): { sShare: SignatureShare; dShare: DShare } {
  const shares = share.share.split(secondaryDelimeter);
  validateSharesLength(shares, 2, 'SD');

  return {
    sShare: parseSignatureShare({ to: share.to, from: share.from, share: shares[0] }),
    dShare: parseDShare({ to: share.to, from: share.from, share: shares[1] }),
  };
}

/**
 * convert S and D share to signature share record
 * @param share - S and D share in a object
 * @returns signature share record
 */
export function convertSDShare(share: { sShare: SShare; dShare: DShare }): SignatureShareRecord {
  return {
    to: getParticipantFromIndex(share.dShare.i),
    from: getParticipantFromIndex(share.dShare.j),
    share: `${share.sShare.R}${delimeter}${share.sShare.s}${delimeter}${share.sShare.y}${secondaryDelimeter}${share.dShare.delta}${delimeter}${share.dShare.Gamma}`,
  };
}

/**
 * parses signature share from signature share record
 * @param share - signature share record
 * @returns Signature Share
 */
export function parseSignatureShare(share: SignatureShareRecord): SignatureShare {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 3, 'Signature');

  return {
    i: getParticipantIndex(share.to),
    R: shares[0],
    s: shares[1],
    y: shares[2],
  };
}

/**
 * convets combined signature to signature share record
 * @param signature - combined signature share
 * @param userIndex - user index, either 1 (user) or 2 (backup)
 * @returns signature share record
 */
export function convertCombinedSignature(signature: Signature, userIndex: number): SignatureShareRecord {
  return {
    to: SignatureShareType.BITGO,
    from: getParticipantFromIndex(userIndex),
    share: `${signature.recid}${delimeter}${signature.r}${delimeter}${signature.s}${delimeter}${signature.y}`,
  };
}

export function parseCombinedSignature(share: SignatureShareRecord): Signature {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 3, 'Signature');

  return {
    recid: Number(shares[0]),
    r: shares[1],
    s: shares[2],
    y: shares[3],
  };
}

/**
 * convert signature share to signature share record
 * @param share - Signature share
 * @param senderIndex
 * @param recipientIndex
 * @returns signature share record
 */
export function convertSignatureShare(
  share: SignatureShare,
  senderIndex: number,
  recipientIndex: number
): SignatureShareRecord {
  return {
    to: getParticipantFromIndex(recipientIndex),
    from: getParticipantFromIndex(senderIndex),
    share: `${share.R}${delimeter}${share.s}${delimeter}${share.y}`,
  };
}

/**
 * converts B share to signature share record
 * @param share - B share
 * @returns signature share record
 */
export function convertBShare(share: BShare): SignatureShareRecord {
  return {
    to: SignatureShareType.BITGO,
    from: getParticipantFromIndex(share.i),
    share: `${share.beta}${delimeter}${share.gamma}${delimeter}${share.k}${delimeter}${share.nu}${delimeter}${
      share.w
    }${delimeter}${share.y}${delimeter}${share.l}${delimeter}${share.m}${delimeter}${share.n}${delimeter}${
      share.ntilde
    }${delimeter}${share.h1}${delimeter}${share.h2}${delimeter}${share.ck}${delimeter}${(share.p || []).join(
      delimeter
    )}`,
  };
}

/**
 * parses B share from signature share record
 * @param share B share record
 * @returns B Share
 */
export function parseBShare(share: SignatureShareRecord): BShare {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 13 + EcdsaPaillierProof.m, 'B');

  return {
    i: getParticipantIndex(share.to),
    beta: shares[0],
    gamma: shares[1],
    k: shares[2],
    nu: shares[3],
    w: shares[4],
    y: shares[5],
    l: shares[6],
    m: shares[7],
    n: shares[8],
    ntilde: shares[9],
    h1: shares[10],
    h2: shares[11],
    ck: shares[12],
    p: shares.slice(13, 13 + EcdsaPaillierProof.m),
  };
}

/**
 * converts O share to signature share record
 * @param share O share
 * @returns signature share record
 */
export function convertOShare(share: OShare): SignatureShareRecord {
  return {
    to: SignatureShareType.BITGO,
    from: getParticipantFromIndex(share.i),
    share: `${share.Gamma}${delimeter}${share.delta}${delimeter}${share.k}${delimeter}${share.omicron}${delimeter}${share.y}`,
  };
}

/**
 * parses O share from signature share record
 * @param share O share record
 * @returns O Share
 */
export function parseOShare(share: SignatureShareRecord): OShare {
  const shares = share.share.split(delimeter);
  validateSharesLength(shares, 5, 'O');

  return {
    i: getParticipantIndex(share.to),
    Gamma: shares[0],
    delta: shares[1],
    k: shares[2],
    omicron: shares[3],
    y: shares[4],
  };
}

/**
 * gets participant index
 * @param participant - participants (user, backup, or bitgo)
 * @returns index (1, 2, 0r 3)
 */
export function getParticipantIndex(participant: 'user' | 'backup' | 'bitgo'): number {
  switch (participant) {
    case 'user':
      return 1;
    case 'backup':
      return 2;
    case 'bitgo':
      return 3;
    default:
      throw Error('Unkown participant');
  }
}

/**
 * gets participant name by index
 * @param index participant index
 * @returns participant name
 */
export function getParticipantFromIndex(index: number): SignatureShareType {
  switch (index) {
    case 1:
      return SignatureShareType.USER;
    case 2:
      return SignatureShareType.BACKUP;
    case 3:
      return SignatureShareType.BITGO;
    default:
      throw new Error(`Unknown participant index ${index}`);
  }
}

/**
 * Helper function to verify u-value wallet signatures for the bitgo-user and bitgo-backup shares.
 * @param params
 */
export async function verifyWalletSignature(params: {
  walletSignature: pgp.Key;
  bitgoPub: pgp.Key;
  commonKeychain: string;
  userKeyId: string;
  backupKeyId: string;
  decryptedShare: string;
  verifierIndex: 1 | 2;
}): Promise<void> {
  const rawNotations = await commonVerifyWalletSignature(params);
  const publicUValueRawNotationIndex = 2 + params.verifierIndex;

  // Derive public form of u-value
  const publicUValue = ecc.pointFromScalar(Buffer.from(params.decryptedShare.slice(0, 64), 'hex'), true);
  assert(publicUValue !== null, 'null public u-value');
  // Verify that the u value + chaincode is equal to the proof retrieved from the raw notations
  assert(
    Buffer.from(publicUValue).toString('hex') + params.decryptedShare.slice(64) ===
      Buffer.from(rawNotations[publicUValueRawNotationIndex].value).toString(),
    'bitgo share mismatch'
  );
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!