signSchnorr method

List<int> signSchnorr(
  1. List<int> digest, {
  2. List<int>? extraEntropy,
})

Signs the given transaction digest using Schnorr signature (old style).

This method is primarily useful for networks like Bitcoin Cash (BCH) that support Schnorr signatures in a legacy format.

  • digest: The transaction digest (message) to sign.

Implementation

List<int> signSchnorr(List<int> digest, {List<int>? extraEntropy}) {
  if (digest.length != BitcoinSignerUtils.baselen) {
    throw CryptoSignException(
        "The digest must be a ${BitcoinSignerUtils.baselen}-byte array.");
  }
  BigInt d = _signingKey.privateKey.secretMultiplier;
  final BigInt order = CryptoSignerConst.generatorSecp256k1.order!;

  if (!(BigInt.one <= d && d <= order - BigInt.one)) {
    throw const CryptoSignException(
        "The secret key must be an integer in the range 1..n-1.");
  }
  extraEntropy ??= CryptoSignerConst.bchSchnorrRfc6979ExtraData;
  BigInt k = RFC6979.generateK(
      order: order,
      secexp: _signingKey.privateKey.secretMultiplier,
      hashFunc: () => SHA256(),
      data: digest,
      extraEntropy: extraEntropy);
  final R = (CryptoSignerConst.generatorSecp256k1 * k);
  if (ECDSAUtils.jacobi(R.y, CryptoSignerConst.curveSecp256k1.p).isNegative) {
    k = order - k;
  }

  // Step 4: Compute e = SHA256(R || pubkey || digest)
  final eHash = QuickCrypto.sha256Hash([
    ...R.toXonly(),
    ...verifierKey._verifyKey.publicKey.toBytes(),
    ...digest
  ]);
  final BigInt e = BigintUtils.fromBytes(eHash) % order;

  // Step 5: Compute Schnorr Signature: s = k + e * d (mod n)
  final BigInt s = (k + e * d) % order;
  final signature = BitcoinSchnorrSignature(r: R.x, s: s).toBytes();

  final verify = verifierKey.verifySchnorrSignature(
      digest: digest, signature: signature);
  if (!verify) {
    throw const CryptoSignException(
        'The created signature does not pass verification.');
  }
  // Step 6: Return Signature (64 bytes: R.x || s)
  return signature;
}