Source: src/Schnorr-signature/Schnorr_Signature.js

/**
 * @fileoverview Schnorr signature implementation for Bitcoin (BIP340)
 * 
 * This module implements Schnorr signatures according to BIP340 specification,
 * providing a more efficient and privacy-friendly alternative to ECDSA.
 * Schnorr signatures enable key aggregation, signature aggregation, and
 * improved multi-signature schemes.
 * 
 * @see {@link https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki|BIP340 - Schnorr Signatures for secp256k1}
 * @see {@link https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1_schnorrsig.h|libsecp256k1 Schnorr API}
 * @author yfbsei
 * @version 1.0.0
 */

import { randomBytes } from 'node:crypto';
import { schnorr } from '@noble/curves/secp256k1';
import { privateKey_decode } from '../utilities/decodeKeys.js';

/**
 * Schnorr signature operations for Bitcoin according to BIP340
 * 
 * Provides comprehensive Schnorr signature functionality including:
 * - Deterministic and randomized signature generation
 * - Public key derivation from private keys
 * - Signature verification with proper point validation
 * - Integration with Bitcoin's Taproot upgrade
 * 
 * Key advantages over ECDSA:
 * - Linear signature aggregation
 * - Smaller signature size (64 bytes vs 71-73 bytes for ECDSA)
 * - Batch verification for improved performance
 * - Eliminates signature malleability
 * - Enables more sophisticated multi-signature schemes
 * 
 * @namespace schnorr_sig
 * @example
 * // Basic Schnorr signature workflow
 * const privateKey = "L1vHfV6GUbMJSvFaqjnButzwq5x4ThdFaotpUgsfScwMNKjdGVuS";
 * const message = "Hello Schnorr!";
 * 
 * // Sign the message
 * const signature = schnorr_sig.sign(privateKey, message);
 * 
 * // Get the public key
 * const publicKey = schnorr_sig.retrieve_public_key(privateKey);
 * 
 * // Verify the signature
 * const isValid = schnorr_sig.verify(signature, message, publicKey);
 * console.log(isValid); // true
 */
const schnorr_sig = {
    /**
     * Creates a Schnorr signature for a given message using BIP340 specification
     * 
     * The signing process follows BIP340:
     * 1. Parse and validate the private key
     * 2. Compute the public key P = d*G (where d is private key)
     * 3. Generate nonce k using auxiliary randomness (prevents side-channel attacks)
     * 4. Compute R = k*G and ensure R.y is even (BIP340 requirement)
     * 5. Compute challenge e = SHA256(R.x || P || m)
     * 6. Compute signature s = (k + e*d) mod n
     * 7. Return signature as 64-byte array: R.x || s
     * 
     * @param {string} [private_key="L1vHfV6GUbMJSvFaqjnButzwq5x4ThdFaotpUgsfScwMNKjdGVuS"] - WIF-encoded private key
     * @param {string} [msg="Hello world"] - Message to sign (will be UTF-8 encoded)
     * @param {Uint8Array} [auxRand=randomBytes(32)] - 32 bytes of auxiliary randomness for nonce generation
     * @returns {Uint8Array} 64-byte Schnorr signature (32-byte R.x + 32-byte s)
     * @throws {Error} If private key is invalid or signing fails
     * @example
     * // Sign with default randomness
     * const signature = schnorr_sig.sign(privateKey, "Hello Bitcoin!");
     * console.log(signature.length); // 64 bytes
     * 
     * // Sign with custom auxiliary randomness
     * const customAux = new Uint8Array(32).fill(0xaa);
     * const deterministicSig = schnorr_sig.sign(privateKey, "Hello Bitcoin!", customAux);
     * 
     * // Multiple signatures of same message with different aux data will differ
     * const sig1 = schnorr_sig.sign(privateKey, "test", new Uint8Array(32).fill(1));
     * const sig2 = schnorr_sig.sign(privateKey, "test", new Uint8Array(32).fill(2));
     * console.log(Buffer.from(sig1).equals(Buffer.from(sig2))); // false
     */
    sign(private_key = "L1vHfV6GUbMJSvFaqjnButzwq5x4ThdFaotpUgsfScwMNKjdGVuS", msg = "Hello world", auxRand = randomBytes(32)) {
        msg = Buffer.from(msg);
        private_key = privateKey_decode(private_key);
        return schnorr.sign(msg, private_key, auxRand)
    },

    /**
     * Verifies a Schnorr signature against a message and public key
     * 
     * The verification process implements BIP340 algorithm:
     * 1. Parse the 64-byte signature into R.x (32 bytes) and s (32 bytes)
     * 2. Validate that R.x and s are valid field elements
     * 3. Compute challenge e = SHA256(R.x || P || m)
     * 4. Compute point S = s*G - e*P
     * 5. Verify that S.x == R.x and S.y is even
     * 
     * This verification is more efficient than ECDSA and allows for batch
     * verification when verifying multiple signatures simultaneously.
     * 
     * @param {Uint8Array|Buffer} sig - 64-byte Schnorr signature to verify
     * @param {string} [msg="Hello World"] - Original message that was signed
     * @param {Uint8Array|Buffer} public_key - 32-byte x-only public key (BIP340 format)
     * @returns {boolean} True if signature is valid, false otherwise
     * @example
     * // Standard verification
     * const signature = schnorr_sig.sign(privateKey, "Hello Schnorr!");
     * const publicKey = schnorr_sig.retrieve_public_key(privateKey);
     * const isValid = schnorr_sig.verify(signature, "Hello Schnorr!", publicKey);
     * console.log(isValid); // true
     * 
     * // Invalid signature detection
     * const invalidSig = new Uint8Array(64); // All zeros
     * const isInvalid = schnorr_sig.verify(invalidSig, "test", publicKey);
     * console.log(isInvalid); // false
     * 
     * // Wrong message detection
     * const wrongMsg = schnorr_sig.verify(signature, "Wrong message", publicKey);
     * console.log(wrongMsg); // false
     */
    verify(sig, msg = "Hello World", public_key) {
        msg = Buffer.from(msg);
        return schnorr.verify(sig, msg, public_key)
    },

    /**
     * Derives the Schnorr public key from a private key according to BIP340
     * 
     * Computes the x-only public key representation used in BIP340:
     * 1. Compute the full public key point P = d*G
     * 2. If P.y is odd, negate d to make P.y even
     * 3. Return only the x-coordinate (32 bytes)
     * 
     * The x-only representation saves space and simplifies signature verification
     * while maintaining the same security properties as full public keys.
     * 
     * @param {string} [private_key="L1vHfV6GUbMJSvFaqjnButzwq5x4ThdFaotpUgsfScwMNKjdGVuS"] - WIF-encoded private key
     * @returns {Uint8Array} 32-byte x-only public key for use with Schnorr signatures
     * @throws {Error} If private key is invalid
     * @example
     * // Get public key for Schnorr operations
     * const privateKey = "L1vHfV6GUbMJSvFaqjnButzwq5x4ThdFaotpUgsfScwMNKjdGVuS";
     * const publicKey = schnorr_sig.retrieve_public_key(privateKey);
     * console.log(publicKey.length); // 32 bytes (x-only)
     * 
     * // Use in signature verification
     * const message = "Taproot transaction";
     * const signature = schnorr_sig.sign(privateKey, message);
     * const verified = schnorr_sig.verify(signature, message, publicKey);
     * console.log(verified); // true
     * 
     * // Compare with different private key
     * const otherPrivKey = "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn";
     * const otherPubKey = schnorr_sig.retrieve_public_key(otherPrivKey);
     * console.log(Buffer.from(publicKey).equals(Buffer.from(otherPubKey))); // false
     */
    retrieve_public_key(private_key = "L1vHfV6GUbMJSvFaqjnButzwq5x4ThdFaotpUgsfScwMNKjdGVuS") {
        private_key = privateKey_decode(private_key);
        return schnorr.getPublicKey(private_key)
    }
}

export default schnorr_sig;