signBip340 method
Signs a given digest using the BIP-340 (Schnorr) signature scheme.
This method follows the BIP-340 specification for creating Schnorr
signatures. It ensures that the provided digest has the correct length
and applies private key tweaking if a tapTweakHash
is provided.
digest
: The 32-byte message digest to be signed. It must be exactlyBitcoinSignerUtils.baselen
bytes in length.tapTweakHash
: (Optional) A tweak applied to the private key for Taproot-related signatures.aux
: (Optional) Auxiliary random data used to add entropy to the signature for security against side-channel attacks.
Implementation
List<int> signBip340(
{required List<int> digest, List<int>? tapTweakHash, List<int>? aux}) {
if (digest.length != BitcoinSignerUtils.baselen) {
throw CryptoSignException(
"The digest must be a ${BitcoinSignerUtils.baselen}-byte array.");
}
if (aux != null && aux.length != 32) {
throw CryptoSignException("The aux must be a 32-byte array.");
}
List<int> byteKey = <int>[];
if (tapTweakHash != null) {
byteKey = BitcoinSignerUtils.calculatePrivateTweek(
_signingKey.privateKey.toBytes(), tapTweakHash);
} else {
byteKey = _signingKey.privateKey.toBytes();
}
aux ??= QuickCrypto.sha256Hash(<int>[...digest, ...byteKey]);
final d0 = BigintUtils.fromBytes(byteKey);
if (!(BigInt.one <= d0 && d0 <= BitcoinSignerUtils.order - BigInt.one)) {
throw const CryptoSignException(
"The secret key must be an integer in the range 1..n-1.");
}
final P = BitcoinSignerUtils.generator * d0;
BigInt d = d0;
if (P.y.isOdd) {
d = BitcoinSignerUtils.order - d;
}
final t = BytesUtils.xor(
BigintUtils.toBytes(d, length: BitcoinSignerUtils.baselen),
P2TRUtils.taggedHash("BIP0340/aux", aux));
final kHash = P2TRUtils.taggedHash(
"BIP0340/nonce",
<int>[
...t,
...BigintUtils.toBytes(P.x, length: BitcoinSignerUtils.baselen),
...digest
],
);
final k0 = BigintUtils.fromBytes(kHash) % BitcoinSignerUtils.order;
if (k0 == BigInt.zero) {
throw const CryptoSignException(
'Failure. This happens only with negligible probability.');
}
final R = (BitcoinSignerUtils.generator * k0);
BigInt k = k0;
if (R.y.isOdd) {
k = BitcoinSignerUtils.order - k;
}
final eHash = P2TRUtils.taggedHash(
"BIP0340/challenge",
List<int>.from([...R.toXonly(), ...P.toXonly(), ...digest]),
);
final e = BigintUtils.fromBytes(eHash) % BitcoinSignerUtils.order;
final eKey = (k + e * d) % BitcoinSignerUtils.order;
final signature = [
...R.toXonly(),
...BigintUtils.toBytes(eKey, length: BitcoinSignerUtils.baselen)
];
if (verifierKey.verifyBip340Signature(
digest: digest, signature: signature, tapTweakHash: tapTweakHash)) {
return signature;
}
throw const CryptoSignException(
'The created signature does not pass verification.');
}