PHP WebShell

Текущая директория: /opt/BitGoJS/modules/abstract-utxo/src

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

/*

These are actually not utxo-specific and belong in a more general module.

 */
import assert from 'assert';

import buildDebug from 'debug';
import * as utxolib from '@bitgo/utxo-lib';
import { bip32 } from '@bitgo/utxo-lib';
import * as bitcoinMessage from 'bitcoinjs-message';
import { BitGoBase, decryptKeychainPrivateKey, KeyIndices } from '@bitgo/sdk-core';

import { ParsedTransaction, VerifyKeySignaturesOptions, VerifyUserPublicKeyOptions } from './abstractUtxoCoin';
import { UtxoKeychain } from './keychains';

const debug = buildDebug('bitgo:abstract-utxo:verifyKey');

/**
 * Verify signatures produced by the user key over the backup and bitgo keys.
 *
 * If set, these signatures ensure that the wallet keys cannot be changed after the wallet has been created.
 * @param {VerifyKeySignaturesOptions} params
 * @return {{backup: boolean, bitgo: boolean}}
 */
export function verifyKeySignature(params: VerifyKeySignaturesOptions): boolean {
  // first, let's verify the integrity of the user key, whose public key is used for subsequent verifications
  const { userKeychain, keychainToVerify, keySignature } = params;
  if (!userKeychain) {
    throw new Error('user keychain is required');
  }

  if (!keychainToVerify) {
    throw new Error('keychain to verify is required');
  }

  if (!keySignature) {
    throw new Error('key signature is required');
  }

  // verify the signature against the user public key
  assert(userKeychain.pub);
  const publicKey = bip32.fromBase58(userKeychain.pub).publicKey;
  // Due to interface of `bitcoinMessage`, we need to convert the public key to an address.
  // Note that this address has no relationship to on-chain transactions. We are
  // only interested in the address as a representation of the public key.
  const signingAddress = utxolib.address.toBase58Check(
    utxolib.crypto.hash160(publicKey),
    utxolib.networks.bitcoin.pubKeyHash,
    // we do not pass `this.network` here because it would fail for zcash
    // the bitcoinMessage library decodes the address and throws away the first byte
    // because zcash has a two-byte prefix, verify() decodes zcash addresses to an invalid pubkey hash
    utxolib.networks.bitcoin
  );

  // BG-5703: use BTC mainnet prefix for all key signature operations
  // (this means do not pass a prefix parameter, and let it use the default prefix instead)
  assert(keychainToVerify.pub);
  try {
    return bitcoinMessage.verify(keychainToVerify.pub, signingAddress, Buffer.from(keySignature, 'hex'));
  } catch (e) {
    debug('error thrown from bitcoinmessage while verifying key signature', e);
    return false;
  }
}

/**
 * Verify signatures against the user private key over the change wallet extended keys
 * @param {ParsedTransaction} tx
 * @param {Keychain} userKeychain
 * @return {boolean}
 * @protected
 */
export function verifyCustomChangeKeySignatures<TNumber extends number | bigint>(
  tx: ParsedTransaction<TNumber>,
  userKeychain: UtxoKeychain
): boolean {
  if (!tx.customChange) {
    throw new Error('parsed transaction is missing required custom change verification data');
  }

  if (!Array.isArray(tx.customChange.keys) || !Array.isArray(tx.customChange.signatures)) {
    throw new Error('customChange property is missing keys or signatures');
  }

  for (const keyIndex of [KeyIndices.USER, KeyIndices.BACKUP, KeyIndices.BITGO]) {
    const keychainToVerify = tx.customChange.keys[keyIndex];
    const keySignature = tx.customChange.signatures[keyIndex];
    if (!keychainToVerify) {
      throw new Error(`missing required custom change ${KeyIndices[keyIndex].toLowerCase()} keychain public key`);
    }
    if (!keySignature) {
      throw new Error(`missing required custom change ${KeyIndices[keyIndex].toLowerCase()} keychain signature`);
    }
    if (!verifyKeySignature({ userKeychain, keychainToVerify, keySignature })) {
      debug('failed to verify custom change %s key signature!', KeyIndices[keyIndex].toLowerCase());
      return false;
    }
  }

  return true;
}

/**
 * Decrypt the wallet's user private key and verify that the claimed public key matches
 * @param {BitGoBase} bitgo
 * @param {VerifyUserPublicKeyOptions} params
 * @return {boolean}
 * @protected
 */
export function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKeyOptions): boolean {
  const { userKeychain, txParams, disableNetworking } = params;
  if (!userKeychain) {
    throw new Error('user keychain is required');
  }

  const userPub = userKeychain.pub;

  // decrypt the user private key, so we can verify that the claimed public key is a match
  let userPrv = userKeychain.prv;
  if (!userPrv && txParams.walletPassphrase) {
    userPrv = decryptKeychainPrivateKey(bitgo, userKeychain, txParams.walletPassphrase);
  }

  if (!userPrv) {
    const errorMessage = 'user private key unavailable for verification';
    if (disableNetworking) {
      console.log(errorMessage);
      return false;
    } else {
      throw new Error(errorMessage);
    }
  } else {
    const userPrivateKey = bip32.fromBase58(userPrv);
    if (userPrivateKey.toBase58() === userPrivateKey.neutered().toBase58()) {
      throw new Error('user private key is only public');
    }
    if (userPrivateKey.neutered().toBase58() !== userPub) {
      throw new Error('user private key does not match public key');
    }
  }

  return true;
}

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


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