import { sha256 } from "@noble/hashes/sha256" ; import * as secp from "./secp256k1" ; import { assertBool , assertBytes , hexToBytes , toHex } from "./utils" ; const bytesToNumber = ( bytes ) => hexToNumber ( toHex ( bytes ) ) ; const numberToHex = ( num ) => num . toString ( 16 ) . padStart ( 64 , "0" ) ; const numberToBytes = ( num ) => hexToBytes ( numberToHex ( num ) ) ; function hexToNumber ( hex ) { if ( typeof hex !== "string" ) { throw new TypeError ( "hexToNumber: expected string, got " + typeof hex ) ; } return BigInt ( `0x${ hex }` ) ; } function mod ( a , b = secp . CURVE . P ) { const result = a % b ; return result >= 0 ? result : b + result ; } const ORDER = secp . CURVE . n ; type Output = Uint8Array | ( ( len : number ) => Uint8Array ) ; interface Signature { signature : Uint8Array ; recid : number ; } function output ( out = ( len ) => new Uint8Array ( len ) , length , value ? ) { if ( typeof out === "function" ) { out = out ( length ) ; } assertBytes ( out , length ) ; if ( value ) { out . set ( value ) ; } return out ; } function getSignature ( signature ) { assertBytes ( signature , 64 ) ; return secp . Signature . fromCompact ( signature ) ; } export function createPrivateKeySync ( ) { return secp . utils . randomPrivateKey ( ) ; } export async function createPrivateKey ( ) { return createPrivateKeySync ( ) ; } export function privateKeyVerify ( privateKey ) { assertBytes ( privateKey , 32 ) ; return secp . utils . isValidPrivateKey ( privateKey ) ; } export function publicKeyCreate ( privateKey , compressed = true , out ? ) { assertBytes ( privateKey , 32 ) ; assertBool ( compressed ) ; const res = secp . getPublicKey ( privateKey , compressed ) ; return output ( out , compressed ? 33 : 65 , res ) ; } export function publicKeyVerify ( publicKey ) { assertBytes ( publicKey , 33 , 65 ) ; try { secp . Point . fromHex ( publicKey ) ; return true ; } catch ( e ) { return false ; } } export function publicKeyConvert ( publicKey , compressed = true , out ? ) { assertBytes ( publicKey , 33 , 65 ) ; assertBool ( compressed ) ; const res = secp . Point . fromHex ( publicKey ) . toRawBytes ( compressed ) ; return output ( out , compressed ? 33 : 65 , res ) ; } export function ecdsaSign ( msgHash , privateKey , options = { noncefn : undefined , data : undefined } , out ? ) { assertBytes ( msgHash , 32 ) ; assertBytes ( privateKey , 32 ) ; if ( typeof options !== "object" || options === null ) { throw new TypeError ( "secp256k1.ecdsaSign: options should be object" ) ; } if ( options && ( options . noncefn !== undefined || options . data !== undefined ) ) { throw new Error ( "Secp256k1: noncefn && data is unsupported" ) ; } const [ signature , recid ] = secp . signSync ( msgHash , privateKey , { recovered : true , der : false } ) ; return { signature : output ( out , 64 , signature ) , recid } ; } export function ecdsaRecover ( signature , recid , msgHash , compressed = true , out ? ) { assertBytes ( msgHash , 32 ) ; assertBool ( compressed ) ; const sign = getSignature ( signature ) . toHex ( ) ; const point = secp . Point . fromSignature ( msgHash , sign , recid ) ; return output ( out , compressed ? 33 : 65 , point . toRawBytes ( compressed ) ) ; } export function ecdsaVerify ( signature , msgHash , publicKey ) { assertBytes ( signature , 64 ) ; assertBytes ( msgHash , 32 ) ; assertBytes ( publicKey , 33 , 65 ) ; assertBytes ( signature , 64 ) ; const r = bytesToNumber ( signature . slice ( 0 , 32 ) ) ; const s = bytesToNumber ( signature . slice ( 32 , 64 ) ) ; if ( r >= ORDER || s >= ORDER ) { throw new Error ( "Cannot parse signature" ) ; } const pub = secp . Point . fromHex ( publicKey ) ; let sig ; try { sig = getSignature ( signature ) ; } catch ( error ) { return false ; } return secp . verify ( sig , msgHash , pub ) ; } export function privateKeyTweakAdd ( privateKey , tweak ) { assertBytes ( privateKey , 32 ) ; assertBytes ( tweak , 32 ) ; let bn = bytesToNumber ( tweak ) ; if ( bn === 0n ) { throw new Error ( "Tweak must not be zero" ) ; } if ( bn >= ORDER ) { throw new Error ( "Tweak bigger than curve order" ) ; } bn += bytesToNumber ( privateKey ) ; if ( bn >= ORDER ) { bn -= ORDER ; } if ( bn === 0n ) { throw new Error ( "The tweak was out of range or the resulted private key is invalid" ) ; } privateKey . set ( hexToBytes ( numberToHex ( bn ) ) ) ; return privateKey ; } export function privateKeyNegate ( privateKey ) { assertBytes ( privateKey , 32 ) ; const bn = mod ( - bytesToNumber ( privateKey ) , ORDER ) ; privateKey . set ( hexToBytes ( numberToHex ( bn ) ) ) ; return privateKey ; } export function publicKeyNegate ( publicKey , compressed = true , out ? ) { assertBytes ( publicKey , 33 , 65 ) ; assertBool ( compressed ) ; const point = secp . Point . fromHex ( publicKey ) . negate ( ) ; return output ( out , compressed ? 33 : 65 , point . toRawBytes ( compressed ) ) ; } export function publicKeyCombine ( publicKeys , compressed = true , out ? ) { if ( ! Array . isArray ( publicKeys ) || ! publicKeys . length ) { throw new TypeError ( `Expected array with one or more items, not ${ publicKeys }` ) ; } for ( const publicKey of publicKeys ) { assertBytes ( publicKey , 33 , 65 ) ; } assertBool ( compressed ) ; const combined = publicKeys . map ( pub => secp . Point . fromHex ( pub ) ) . reduce ( ( res , curr ) => res . add ( curr ) , secp . Point . ZERO ) ; return output ( out , compressed ? 33 : 65 , combined . toRawBytes ( compressed ) ) ; } export function publicKeyTweakAdd ( publicKey , tweak , compressed = true , out ? ) { assertBytes ( publicKey , 33 , 65 ) ; assertBytes ( tweak , 32 ) ; assertBool ( compressed ) ; const p1 = secp . Point . fromHex ( publicKey ) ; const p2 = secp . Point . fromPrivateKey ( tweak ) ; const point = p1 . add ( p2 ) ; if ( p2 . equals ( secp . Point . ZERO ) || point . equals ( secp . Point . ZERO ) ) { throw new Error ( "Tweak must not be zero" ) ; } return output ( out , compressed ? 33 : 65 , point . toRawBytes ( compressed ) ) ; } export function publicKeyTweakMul ( publicKey , tweak , compressed = true , out ? ) { assertBytes ( publicKey , 33 , 65 ) ; assertBytes ( tweak , 32 ) ; assertBool ( compressed ) ; const bn = bytesToNumber ( tweak ) ; if ( bn === 0n ) { throw new Error ( "Tweak must not be zero" ) ; } if ( bn <= 1 || bn >= ORDER ) { throw new Error ( "Tweak is zero or bigger than curve order" ) ; } const point = secp . Point . fromHex ( publicKey ) . multiply ( bn ) ; return output ( out , compressed ? 33 : 65 , point . toRawBytes ( compressed ) ) ; } export function privateKeyTweakMul ( privateKey , tweak ) { assertBytes ( privateKey , 32 ) ; assertBytes ( tweak , 32 ) ; const bn = bytesToNumber ( tweak ) ; if ( bn <= 1 || bn >= ORDER ) { throw new Error ( "Tweak is zero or bigger than curve order" ) ; } const res = mod ( bn * bytesToNumber ( privateKey ) , ORDER ) ; if ( res === 0n ) { throw new Error ( "The tweak was out of range or the resulted private key is invalid" ) ; } privateKey . set ( hexToBytes ( numberToHex ( res ) ) ) ; return privateKey ; } export function signatureExport ( signature , out ? ) { const res = getSignature ( signature ) . toRawBytes ( ) ; return output ( out , 72 , getSignature ( signature ) . toRawBytes ( ) ) . slice ( 0 , res . length ) ; } export function signatureImport ( signature , out ? ) { assertBytes ( signature ) ; const sig = secp . Signature . fromDER ( signature ) ; return output ( out , 64 , hexToBytes ( sig . toCompactHex ( ) ) ) ; } export function signatureNormalize ( signature ) { const res = getSignature ( signature ) ; if ( res . s > ORDER / 2n ) { signature . set ( numberToBytes ( ORDER - res . s ) , 32 ) ; } return signature ; } export function ecdh ( publicKey , privateKey , options = { } , out ? ) { assertBytes ( publicKey , 33 , 65 ) ; assertBytes ( privateKey , 32 ) ; if ( typeof options !== "object" || options === null ) { throw new TypeError ( "secp256k1.ecdh: options should be object" ) ; } if ( options . data !== undefined ) { assertBytes ( options . data ) ; } const point = secp . Point . fromHex ( secp . getSharedSecret ( privateKey , publicKey ) ) ; if ( options . hashfn === undefined ) { return output ( out , 32 , sha256 ( point . toRawBytes ( true ) ) ) ; } if ( typeof options . hashfn !== "function" ) { throw new TypeError ( "secp256k1.ecdh: options.hashfn should be function" ) ; } if ( options . xbuf !== undefined ) { assertBytes ( options . xbuf , 32 ) ; } if ( options . ybuf !== undefined ) { assertBytes ( options . ybuf , 32 ) ; } assertBytes ( out as Uint8Array , 32 ) ; const xbuf = options . xbuf || new Uint8Array ( 32 ) ; xbuf . set ( numberToBytes ( point . x ) ) ; const ybuf = options . ybuf || new Uint8Array ( 32 ) ; ybuf . set ( numberToBytes ( point . y ) ) ; const hash = options . hashfn ( xbuf , ybuf , options . data ! ) ; if ( ! ( hash instanceof Uint8Array ) || hash . length !== 32 ) { throw new Error ( "secp256k1.ecdh: invalid options.hashfn output" ) ; } return output ( out , 32 , hash ) ; } export function contextRandomize ( seed ) { if ( seed !== null ) { assertBytes ( seed , 32 ) ; } }