PartialSig.sign constructor

PartialSig.sign({
  1. required Uint8List secretKey,
  2. required List<Uint8List> secretNonces,
  3. required NonceSession session,
})

BIP-327 Sign:

  1. Aggregate nonces -> R, compute coefficient b.
  2. e = H("BIP0340/challenge", R_x || Q_x || msg) mod n.
  3. k = k1 + b*k2 (mod n); negate if R has odd y.
  4. d = a * sk (mod n); negate if Q has odd y.
  5. s = k + e*d (mod n).

Implementation

factory PartialSig.sign({
  required Uint8List secretKey,
  required List<Uint8List> secretNonces,
  required NonceSession session,
}) {
  if (secretNonces.length != 2) {
    throw ArgumentError('Expected 2 secret nonces (k1, k2)');
  }

  final pubKeyBytes = VaultKeeper.vault.curve.derivePublicKey(secretKey);
  final signerPubKey = PublicKey(pubKeyBytes);

  final aggR = session.aggregateNonces();
  final rPoint = ecBytesToPoint(aggR);

  EcPoint aggR1 = EcPoint.infinity();
  EcPoint aggR2 = EcPoint.infinity();
  for (final nonce in session.nonces) {
    aggR1 = ecPointAdd(aggR1, ecBytesToPoint(nonce.r1));
    aggR2 = ecPointAdd(aggR2, ecBytesToPoint(nonce.r2));
  }
  // BIP-327 sec. GetSessionValues: noncecoef DOES include the message (b is
  // hashed over aggnonce || Q_x || msg).
  final aggR1Bytes = aggR1.isInfinity ? Uint8List(33) : ecPointToBytes(aggR1, compressed: true);
  final aggR2Bytes = aggR2.isInfinity ? Uint8List(33) : ecPointToBytes(aggR2, compressed: true);
  final bInput = concatBytes(
      [aggR1Bytes, aggR2Bytes, session.aggKey.xOnly, session.message]);
  final bHash = taggedHash('MuSig/noncecoef', bInput);
  final b = bytesToBigInt(bHash) % secp256k1N;

  // e = H("BIP0340/challenge", R_x || Q_x || msg) mod n
  final rX = bigIntToBytes(rPoint.x, 32);
  final challengeInput = concatBytes([
    rX,
    session.aggKey.xOnly,
    session.message,
  ]);
  final eHash = taggedHash('BIP0340/challenge', challengeInput);
  final e = bytesToBigInt(eHash) % secp256k1N;

  // k = k1 + b*k2; negate if R has odd y
  final k1 = bytesToBigInt(secretNonces[0]);
  final k2 = bytesToBigInt(secretNonces[1]);
  BigInt k = (k1 + b * k2) % secp256k1N;
  if (!rPoint.y.isEven) {
    k = (secp256k1N - k) % secp256k1N;
  }

  // d = a * sk; negate if Q has odd y
  final aBytes = session.aggKey.coefficient(signerPubKey);
  final a = bytesToBigInt(aBytes);
  BigInt d = (a * bytesToBigInt(secretKey)) % secp256k1N;
  if (!session.aggKey.yIsEven) {
    d = (secp256k1N - d) % secp256k1N;
  }

  // s = k + e * d (mod n)
  final s = (k + e * d) % secp256k1N;

  return PartialSig(
    signerKey: signerPubKey,
    partialS: bigIntToBytes(s, 32),
  );
}