verifyBip340SignatureUsingXOnly static method
Verifies a BIP-340 Schnorr signature using an x-only public key.
This method checks whether the given Schnorr signature is valid for the
provided message digest and x-only public key. It optionally applies a
tweak if tapTweakHash
is provided.
xOnly
: A 32-byte x-only public key (corresponding to the Taproot key).digest
: A 32-byte message digest that was signed.signature
: A 64-byte Schnorr signature (R.x || s
).tapTweakHash
(optional): A 32-byte tweak hash used to modify the public key.
Implementation
static bool verifyBip340SignatureUsingXOnly(
{required List<int> xOnly,
required List<int> digest,
required List<int> signature,
List<int>? tapTweakHash}) {
if (digest.length != BitcoinSignerUtils.baselen) {
throw CryptoSignException(
"The digest must be a ${BitcoinSignerUtils.baselen}-byte array.");
}
if (xOnly.length != EcdsaKeysConst.pointCoordByteLen) {
throw CryptoSignException("Invalid xOnly bytes length.");
}
final schnorrSignature = BitcoinSchnorrSignature.fromBytes(signature);
final x = BigintUtils.fromBytes(xOnly);
final P = tapTweakHash != null
? tweakKey(xBig: x, tapTweakHash: tapTweakHash)
: P2TRUtils.liftX(x);
final ProjectiveECCPoint generator = BitcoinSignerUtils.generator;
final BigInt prime = BitcoinSignerUtils.generator.curve.p;
if (schnorrSignature.r >= prime ||
schnorrSignature.s >= BitcoinSignerUtils.order) {
return false;
}
final eHash = P2TRUtils.taggedHash(
"BIP0340/challenge",
List<int>.from([...schnorrSignature.rBytes(), ...P.toXonly(), ...digest]),
);
BigInt e = BigintUtils.fromBytes(eHash) % BitcoinSignerUtils.order;
final sp = generator * schnorrSignature.s;
if (P.y.isEven) {
e = BitcoinSignerUtils.order - e;
}
final ProjectiveECCPoint eP = P * e;
final R = sp + eP;
if (R.y.isOdd || R.x != schnorrSignature.r) {
return false;
}
return true;
}