UNPKG

@covenance/dlc

Version:

Crypto and Bitcoin functions for Covenance DLC implementation

107 lines (95 loc) 3.28 kB
import { Point, CURVE } from './secp256k1'; import { PubKey, Signature } from './types'; import { sha256, bytesToHex } from '../utils'; /** * Function to verify a general signature * @param sig - The signature to verify * @param pubKey - The public key to verify against * @param message - The message that was signed * @param tag - The tag to use for hashing (defaults to BIP0340/challenge) * @returns Boolean indicating whether the signature is valid */ export async function verifySig( sig: Signature, pubKey: PubKey, message: Uint8Array, tag: Uint8Array = Uint8Array.from(Buffer.from('7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c', 'hex')) // SHA256('BIP0340/challenge') ): Promise<boolean> { // Compute H(tag||tag||R||P||m) const hashInput = new Uint8Array([ ...tag, ...tag, ...sig.R.toRawBytes(true).slice(1), ...pubKey.toRawBytes(true).slice(1), ...message ]); const hash = await sha256(hashInput); const e = mod(BigInt('0x' + bytesToHex(hash)), CURVE.n); // Compute s * G const sG = Point.BASE.multiply(sig.s); // Compute R + e * P const rightSide = sig.R.add(pubKey.multiply(e)); // Verify s * G = R + e * P return sG.equals(rightSide); } /** * Function to verify a signature with additional script interpreter like verification * @param sig - The signature to verify * @param pubKey - The public key to verify against * @param message - The message that was signed * @param tag - The tag to use for hashing (defaults to BIP0340/challenge) * @returns Boolean indicating whether the signature is valid */ export async function verifySigStrict( sig: Signature, pubKey: PubKey, message: Uint8Array, tag: Uint8Array = Uint8Array.from(Buffer.from('7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c', 'hex')) // SHA256('BIP0340/challenge') ): Promise<boolean> { // First do basic signature verification const basicVerification = await verifySig(sig, pubKey, message, tag); if (!basicVerification) return false; // Perform additional script interpreter like verification const r = sig.R.x; const s = sig.s; if (r >= CURVE.P || s >= CURVE.n) { return false; } const hashInput = new Uint8Array([ ...tag, ...tag, ...sig.R.toRawBytes(true).slice(1), ...pubKey.toRawBytes(true).slice(1), ...message ]); const hash = await sha256(hashInput); const e = mod(BigInt('0x' + bytesToHex(hash)), CURVE.n); const sG = Point.BASE.multiply(sig.s); const R = sG.add(pubKey.multiply(e).negate()); if ( !(R.y % 2n === 0n) || !(R.x === r) ) { return false; } return true; } /** * Encodes a point as a BIP-340 x-only key * @param point - The point to encode * @returns The encoded point as a 32-byte Buffer */ export function encodeXOnlyPubkey({ x, y }: Point): Buffer { if (y & 1n) y = CURVE.P - y; // even-y representative if (x < 0n || x >= CURVE.P) throw new RangeError("x out of range"); return Buffer.from(x.toString(16).padStart(64, "0"), "hex"); } /** * Modulus operation that handles negative numbers * @param k - The number to mod * @param n - The modulus * @returns The modulus of k and n */ export function mod(k: bigint, n: bigint): bigint { return ((k % n) + n) % n; }