Enable and expose Schnorrsig (#44)

* Enable Schnorrsig module in wrapper

The extrakeys module is a dependency for Schnorrsig, so that's enabled as well.

* Add {.bycopy.} pragma

* Add Schnorrsig interface to `abi.nim`

Multikey interface is a dependency the for schnorrsig, so it was added
as well.

* Add tests for Schnorr signing

* Fix schnorr magic const declaration on 1.6 and below

* Remove unnecessary {.bycopy.} pragmas

Done under the impression that {.bycopy.} is not necessary when only
passing the object to C via ptr.

* Make SkKeyPair a wrapper around secp256k1_keypair

* Add more helper procs for new SkKeyPair

* Small fixes

* Re-order

* Rework patch. Implement Schnorr signing and undo breaking changes.

* Reduce code duplication

* Fix type

* Remove accidental extra indentation

* Add `default` {.error.} proc for SkSchnorrSignature

* Remove extra test

* Add from/to raw/hex

* Comments

* Add low-level test for `secp256k1_keypair`

* Fix errors on Nim 1.2

* Comment

* Allow passing a `Rng`/`FoolproofRng` to `signSchnorr` for improved security

* Comments

* Correct `noncefp` to be a pointer in `extraparams` object

* Remove unneeded {.bycopy.}

Co-authored-by: Jacek Sieka <arnetheduck@gmail.com>

* Don't check the RNG for Schnorr sig using private key requirements.

* Add comment detailing that `signSchnorr` without an `rng` is discouraged

* Remove non-`rng` signSchnorr variant from tests

* Rename `signSchnorr` without `rng` to `signSchnorrUnsafe`

* Unify `schnorrSig` implementations and add `array[32, bytes]` variant

* Fix on Nim 1.2

* Make `signSchnorr` accept `Opt[array[32, byte]]` rather than `[array[32,byte]]`

* Remove unused template param

* Inline `signSchnorr Rng` procs

* Remove `nimble.lock`, was breaking tests on Nim >1.6

Was causing
`Error: cannot open file: stew/byteutils`

* Correct template parameter naming

* Consistently apply {.noinit.} pragma

* `{.noinit.}` random byte array

* Revert "`{.noinit.}` random byte array"

This reverts commit a3f99817d9.

* Correct template pragmas

* Explicitly declare `noncefp` as `nil`

* Create and export `xonly_pubkey` wrapping type

* Complete implementation of `SkXOnlyPublicKey`

* Correct comment

* Add tests for 'SkXOnlyPublicKey`

* Correct conversion proc name

* Correct conversion proc name cont.

---------

Co-authored-by: Jacek Sieka <arnetheduck@gmail.com>
This commit is contained in:
Gruruya 2023-04-11 04:17:03 -04:00 committed by GitHub
parent 4c41c5029f
commit 6e18455a63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 401 additions and 30 deletions

View File

@ -1,25 +0,0 @@
{
"version": 1,
"packages": {
"nimcrypto": {
"version": "0.5.4",
"vcsRevision": "a5742a9a214ac33f91615f3862c7b099aec43b00",
"url": "https://github.com/cheatfate/nimcrypto",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "f76c87707cd4e96355b8bb6ef27e7f8b0aac1e08"
}
},
"stew": {
"version": "0.1.0",
"vcsRevision": "cdb1f213d073fd2ecbdaf35a866417657da9294c",
"url": "https://github.com/status-im/nim-stew",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "3dd9e4ec78d906b59b6f9bcf74f5f8fa6b5c24fe"
}
}
}
}

View File

@ -54,12 +54,18 @@ const
SkRawRecoverableSignatureSize* = 65
## Size of recoverable signature in octets (bytes)
SkRawSchnorrSignatureSize* = 64
## Size of Schnorr signature in octets (bytes)
SkRawPublicKeySize* = 65
## Size of uncompressed public key in octets (bytes)
SkRawCompressedPublicKeySize* = 33
## Size of compressed public key in octets (bytes)
SkRawXOnlyPublicKeySize* = 32
## Size of x-only public key in octets (bytes)
SkMessageSize* = 32
## Size of message that can be signed
@ -74,6 +80,10 @@ type
## Representation of public key.
data: secp256k1_pubkey
SkXOnlyPublicKey* {.requiresInit.} = object
## Representation of public key that only reveals the x-coordinate.
data: secp256k1_xonly_pubkey
SkSecretKey* {.requiresInit.} = object
## Representation of secret key.
data: array[SkRawSecretKeySize, byte]
@ -91,6 +101,10 @@ type
## Representation of recoverable signature.
data: secp256k1_ecdsa_recoverable_signature
SkSchnorrSignature* {.requiresInit.} = object
## Representation of a Schnorr signature.
data: array[SkRawSchnorrSignatureSize, byte]
SkContext = object
## Representation of Secp256k1 context object.
context: ptr secp256k1_context
@ -305,6 +319,43 @@ func toRawCompressed*(pubkey: SkPublicKey): array[SkRawCompressedPublicKeySize,
func toHexCompressed*(pubkey: SkPublicKey): string =
toHex(toRawCompressed(pubkey))
func toXOnly*(pk: SkPublicKey): SkXOnlyPublicKey =
## Gets a pubkey that reveals only the x-coordinate on the curve.
var data {.noinit.}: secp256k1_xonly_pubkey
let res = secp256k1_xonly_pubkey_from_pubkey(
secp256k1_context_no_precomp, addr data, nil, unsafeAddr pk.data)
doAssert res == 1, "cannot get xonly pubkey from pubkey, key invalid?"
SkXOnlyPublicKey(data: data)
func fromRaw*(T: type SkXOnlyPublicKey, data: openArray[byte]): SkResult[T] =
## Initialize Secp256k1 `x-only public key` ``key`` from raw binary
## representation ``data``.
if len(data) != SkRawXOnlyPublicKeySize:
return err(static(
&"secp: x-only public key must be {SkRawXOnlyPublicKeySize} bytes"))
var key {.noinit.}: secp256k1_xonly_pubkey
if secp256k1_xonly_pubkey_parse(
secp256k1_context_no_precomp, addr key, data.baseAddr) != 1:
return err("secp: cannot parse x-only public key")
ok(SkXOnlyPublicKey(data: key))
func fromHex*(T: type SkXOnlyPublicKey, data: string): SkResult[T] =
## Initialize Secp256k1 `x-only public key` ``key`` from hexadecimal string
## representation ``data``.
T.fromRaw(? seq[byte].fromHex(data))
func toRaw*(pubkey: SkXOnlyPublicKey): array[SkRawXOnlyPublicKeySize, byte] =
## Serialize Secp256k1 `x-only public key` ``key`` to raw form.
let res = secp256k1_xonly_pubkey_serialize(
secp256k1_context_no_precomp, result.baseAddr, unsafeAddr pubkey.data)
doAssert res == 1, "Can't fail, per documentation"
func toHex*(pubkey: SkXOnlyPublicKey): string =
toHex(toRaw(pubkey))
func fromRaw*(T: type SkSignature, data: openArray[byte]): SkResult[T] =
## Load compact signature from data
if data.len() < SkRawSignatureSize:
@ -392,13 +443,31 @@ func toRaw*(sig: SkRecoverableSignature): array[SkRawRecoverableSignatureSize, b
var recid = cint(0)
let res = secp256k1_ecdsa_recoverable_signature_serialize_compact(
secp256k1_context_no_precomp, result.baseAddr, addr recid, unsafeAddr sig.data)
doAssert res == 1, "can't fail, per documentation"
doAssert res == 1, "Can't fail, per documentation"
result[64] = byte(recid)
func toHex*(sig: SkRecoverableSignature): string =
toHex(toRaw(sig))
func fromRaw*(T: type SkSchnorrSignature, data: openArray[byte]): SkResult[T] =
## Load Schnorr `signature` from data as created by `toRaw`.
if len(data) < SkRawSchnorrSignatureSize:
return err(static(&"secp: raw schnorr signature should be {SkRawSchnorrSignatureSize} bytes"))
ok(T(data: toArray(64, data.toOpenArray(0, SkRawSchnorrSignatureSize - 1))))
func fromHex*(T: type SkSchnorrSignature, data: string): SkResult[T] =
## Initialize Schnorr `signature` from hexadecimal string representation ``data``.
T.fromRaw(? seq[byte].fromHex(data))
func toRaw*(sig: SkSchnorrSignature): array[SkRawSchnorrSignatureSize, byte] =
## Serialize Schnorr `signature` ``sig`` to raw binary form.
sig.data
func toHex*(sig: SkSchnorrSignature): string =
toHex(toRaw(sig))
proc random*(T: type SkKeyPair, rng: Rng): SkResult[T] =
## Generates new random key pair.
let seckey = ? SkSecretKey.random(rng)
@ -423,10 +492,18 @@ func `==`*(lhs, rhs: SkSignature): bool =
## Compare Secp256k1 `signature` objects for equality.
CT.isEqual(lhs.toRaw(), rhs.toRaw())
func `==`*(lhs, rhs: SkXOnlyPublicKey): bool =
## Compare Secp256k1 `x-only public key` objects for equality.
CT.isEqual(lhs.toRaw(), rhs.toRaw())
func `==`*(lhs, rhs: SkRecoverableSignature): bool =
## Compare Secp256k1 `recoverable signature` objects for equality.
CT.isEqual(lhs.toRaw(), rhs.toRaw())
func `==`*(lhs, rhs: SkSchnorrSignature): bool =
## Compare Schnorr signature objects for equality.
CT.isEqual(lhs.toRaw(), rhs.toRaw())
func sign*(key: SkSecretKey, msg: SkMessage): SkSignature =
## Sign message `msg` using private key `key` and return signature object.
## It is recommended that `msg` is produced by hashing the input data to
@ -445,10 +522,83 @@ func signRecoverable*(key: SkSecretKey, msg: SkMessage): SkRecoverableSignature
doAssert res == 1, "cannot create recoverable signature, key invalid?"
SkRecoverableSignature(data: data)
template signSchnorrImpl(signMsg: untyped): untyped =
var kp {.noinit, inject.}: secp256k1_keypair
let res = secp256k1_keypair_create(
getContext(), addr kp, key.data.baseAddr)
doAssert res == 1, "cannot create keypair, key invalid?"
var data {.noinit, inject.}: array[SkRawSchnorrSignatureSize, byte]
let res2 = signMsg
doAssert res2 == 1, "cannot create signature, key invalid?"
SkSchnorrSignature(data: data)
func signSchnorr*(key: SkSecretKey, msg: SkMessage, randbytes: Opt[array[32, byte]]): SkSchnorrSignature =
## Sign message `msg` using private key `key` with the Schnorr signature algorithm and return signature object.
## `randbytes` should be an array of 32 freshly generated random bytes.
let aux_rand32 = if randbytes.isSome: randbytes[].baseAddr else: nil
signSchnorrImpl(
secp256k1_schnorrsig_sign32(
getContext(), data.baseAddr, msg.baseAddr, addr kp, aux_rand32))
func signSchnorr*(key: SkSecretKey, msg: openArray[byte], randbytes: Opt[array[32, byte]]): SkSchnorrSignature =
## Sign message `msg` using private key `key` with the Schnorr signature algorithm and return signature object.
## `randbytes` should be an array of 32 freshly generated random bytes.
let aux_rand32 = if randbytes.isSome: randbytes[].baseAddr else: nil
let extraparams = secp256k1_schnorrsig_extraparams(magic: SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC, noncefp: nil, ndata: aux_rand32)
signSchnorrImpl(
secp256k1_schnorrsig_sign_custom(
getContext(), data.baseAddr, msg.baseAddr, csize_t msg.len, addr kp, unsafeAddr extraparams))
template signSchnorrRngImpl(): untyped =
var randbytes: array[32, byte]
if rng(randbytes):
return ok(signSchnorr(key, msg, Opt.some randbytes))
return err("secp: cannot get random bytes for signature")
proc signSchnorr*(key: SkSecretKey, msg: SkMessage, rng: Rng): SkResult[SkSchnorrSignature] {.inline.} =
## Sign message `msg` using private key `key` with the Schnorr signature algorithm and return signature object.
## Uses ``rng`` to generate 32-bytes of random data for signature generation.
signSchnorrRngImpl()
proc signSchnorr*(key: SkSecretKey, msg: openArray[byte], rng: Rng): SkResult[SkSchnorrSignature] {.inline.} =
## Sign message `msg` using private key `key` with the Schnorr signature algorithm and return signature object.
## Uses ``rng`` to generate 32-bytes of random data for signature generation.
signSchnorrRngImpl()
template signSchnorrFoolproofRngImpl(): untyped =
var randbytes: array[32, byte]
rng(randbytes)
return signSchnorr(key, msg, Opt.some randbytes)
proc signSchnorr*(key: SkSecretKey, msg: SkMessage, rng: FoolproofRng): SkSchnorrSignature {.inline.} =
## Sign message `msg` using private key `key` with the Schnorr signature algorithm and return signature object.
## Uses ``rng`` to generate 32-bytes of random data for signature generation.
signSchnorrFoolproofRngImpl()
proc signSchnorr*(key: SkSecretKey, msg: openArray[byte], rng: FoolproofRng): SkSchnorrSignature {.inline.} =
## Sign message `msg` using private key `key` with the Schnorr signature algorithm and return signature object.
## Uses ``rng`` to generate 32-bytes of random data for signature generation.
signSchnorrFoolproofRngImpl()
func verify*(sig: SkSignature, msg: SkMessage, key: SkPublicKey): bool =
secp256k1_ecdsa_verify(
getContext(), unsafeAddr sig.data, msg.baseAddr, unsafeAddr key.data) == 1
func verify*(sig: SkSchnorrSignature, msg: SkMessage, pubkey: SkXOnlyPublicKey): bool =
secp256k1_schnorrsig_verify(
getContext(), unsafeAddr sig.data[0], msg.baseAddr, csize_t SkMessageSize, unsafeAddr pubkey.data) == 1
func verify*(sig: SkSchnorrSignature, msg: openArray[byte], pubkey: SkXOnlyPublicKey): bool =
secp256k1_schnorrsig_verify(
getContext(), unsafeAddr sig.data[0], msg.baseAddr, csize_t msg.len, unsafeAddr pubkey.data) == 1
template verify*(sig: SkSchnorrSignature, msg: SkMessage, pubkey: SkPublicKey): bool =
verify(sig, msg, pubkey.toXOnly)
template verify*(sig: SkSchnorrSignature, msg: openArray[byte], pubkey: SkPublicKey): bool =
verify(sig, msg, pubkey.toXOnly)
func recover*(sig: SkRecoverableSignature, msg: SkMessage): SkResult[SkPublicKey] =
var data {.noinit.}: secp256k1_pubkey
if secp256k1_ecdsa_recover(
@ -497,7 +647,7 @@ func clear*(v: var SkEcdhRawSecret) =
burnMem(v.data)
func `$`*(
v: SkPublicKey | SkSecretKey | SkSignature | SkRecoverableSignature): string =
v: SkPublicKey | SkSecretKey | SkXOnlyPublicKey | SkSignature | SkRecoverableSignature | SkSchnorrSignature): string =
toHex(v)
func fromBytes*(T: type SkMessage, data: openArray[byte]): SkResult[SkMessage] =
@ -510,8 +660,10 @@ func fromBytes*(T: type SkMessage, data: openArray[byte]): SkResult[SkMessage] =
# TODO replace `requiresInit` with a pragma that does the expected thing
proc default*(T: type SkPublicKey): T {.error: "loophole".}
proc default*(T: type SkSecretKey): T {.error: "loophole".}
proc default*(T: type SkXOnlyPublicKey): T {.error: "loophole".}
proc default*(T: type SkSignature): T {.error: "loophole".}
proc default*(T: type SkRecoverableSignature): T {.error: "loophole".}
proc default*(T: type SkSchnorrSignature): T {.error: "loophole".}
proc default*(T: type SkEcdhSecret): T {.error: "loophole".}
proc default*(T: type SkEcdhRawSecret): T {.error: "loophole".}

View File

@ -326,3 +326,218 @@ proc secp256k1_ecdh_raw*(ctx: ptr secp256k1_context; output32: ptr byte;
## initialized public key
## privkey: a 32-byte scalar with which to multiply the point
##
## Multikey interface follows
type
secp256k1_xonly_pubkey* = object
## Opaque data structure that holds a parsed and valid "x-only" public key.
## An x-only pubkey encodes a point whose Y coordinate is even. It is
## serialized using only its X coordinate (32 bytes). See BIP-340 for more
## information about x-only pubkeys.
##
## The exact representation of data inside is implementation defined and not
## guaranteed to be portable between different platforms or versions. It is
## however guaranteed to be 64 bytes in size, and can be safely copied/moved.
## If you need to convert to a format suitable for storage, transmission, use
## use secp256k1_xonly_pubkey_serialize and secp256k1_xonly_pubkey_parse. To
## compare keys, use secp256k1_xonly_pubkey_cmp.
##
data*: array[64, uint8]
secp256k1_keypair* = object
## Opaque data structure that holds a keypair consisting of a secret and a
## public key.
##
## The exact representation of data inside is implementation defined and not
## guaranteed to be portable between different platforms or versions. It is
## however guaranteed to be 96 bytes in size, and can be safely copied/moved.
##
data*: array[96, uint8]
proc secp256k1_xonly_pubkey_parse*(ctx: ptr secp256k1_context;
pubkey: ptr secp256k1_xonly_pubkey;
input32: ptr byte): cint {.secp.}
## Parse a 32-byte sequence into a xonly_pubkey object.
##
## Returns: 1 if the public key was fully valid.
## 0 if the public key could not be parsed or is invalid.
##
## Args: ctx: a secp256k1 context object.
## Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
## parsed version of input. If not, it's set to an invalid value.
## In: input32: pointer to a serialized xonly_pubkey.
##
proc secp256k1_xonly_pubkey_serialize*(ctx: ptr secp256k1_context;
output32: ptr byte;
pubkey: ptr secp256k1_xonly_pubkey): cint {.secp.}
## Serialize an xonly_pubkey object into a 32-byte sequence.
##
## Returns: 1 always.
##
## Args: ctx: a secp256k1 context object.
## Out: output32: a pointer to a 32-byte array to place the serialized key in.
## In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an initialized public key.
##
proc secp256k1_xonly_pubkey_from_pubkey*(ctx: ptr secp256k1_context;
xonly_pubkey: ptr secp256k1_xonly_pubkey;
pk_parity: ptr cint;
pubkey: ptr secp256k1_pubkey): cint {.secp.}
## Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
##
## Returns: 1 always.
##
## Args: ctx: pointer to a context object.
## Out: xonly_pubkey: pointer to an x-only public key object for placing the converted public key.
## pk_parity: Ignored if NULL. Otherwise, pointer to an integer that
## will be set to 1 if the point encoded by xonly_pubkey is
## the negation of the pubkey and set to 0 otherwise.
## In: pubkey: pointer to a public key that is converted.
##
proc secp256k1_xonly_pubkey_tweak_add*(ctx: ptr secp256k1_context;
output_pubkey: ptr secp256k1_pubkey;
internal_pubkey: ptr secp256k1_xonly_pubkey;
tweak32: ptr byte): cint {.secp.}
proc secp256k1_xonly_pubkey_tweak_add_check*(ctx: ptr secp256k1_context;
tweaked_pubkey32: ptr byte;
tweaked_pk_parity: cint;
internal_pubkey: ptr secp256k1_xonly_pubkey;
tweak32: ptr byte): cint {.secp.}
proc secp256k1_keypair_create*(ctx: ptr secp256k1_context;
keypair: ptr secp256k1_keypair;
seckey: ptr byte): cint {.secp.}
## Compute the keypair for a secret key.
##
## Returns: 1: secret was valid, keypair is ready to use
## 0: secret was invalid, try again with a different secret
## Args: ctx: pointer to a context object, initialized for signing.
## Out: keypair: pointer to the created keypair.
## In: seckey: pointer to a 32-byte secret key.
##
proc secp256k1_keypair_sec*(ctx: ptr secp256k1_context;
seckey: ptr byte;
keypair: ptr secp256k1_keypair): cint {.secp.}
proc secp256k1_keypair_pub*(ctx: ptr secp256k1_context;
pubkey: ptr secp256k1_pubkey;
keypair: ptr secp256k1_keypair): cint {.secp.}
proc secp256k1_keypair_xonly_pub*(ctx: ptr secp256k1_context;
pubkey: ptr secp256k1_xonly_pubkey;
pk_parity: ptr cint;
keypair: ptr secp256k1_keypair): cint {.secp.}
proc secp256k1_keypair_xonly_tweak_add*(ctx: ptr secp256k1_context;
keypair: ptr secp256k1_keypair;
tweak32: ptr byte): cint {.secp.}
## Schnorrsig interface follows
type
secp256k1_nonce_function_hardened* = object
nonce32*: ptr byte
msg*: ptr byte
msglen*: csize_t
key32*: ptr byte
xonly_pk32*: ptr byte
algo*: ptr byte
algolen*: csize_t
data*: pointer
const
SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC* = [ 0xda'u8 , 0x6f, 0xb3, 0x8c ]
type
secp256k1_schnorrsig_extraparams* = object
## Data structure that contains additional arguments for schnorrsig_sign_custom.
##
## Members:
## magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
## and has no other function than making sure the object is
## initialized.
## noncefp: pointer to a nonce generation function. If NULL,
## secp256k1_nonce_function_bip340 is used
## ndata: pointer to arbitrary data used by the nonce generation function
## (can be NULL). If it is non-NULL and
## secp256k1_nonce_function_bip340 is used, then ndata must be a
## pointer to 32-byte auxiliary randomness as per BIP-340.
##
magic*: array[4, uint8]
noncefp*: ptr secp256k1_nonce_function_hardened
ndata*: pointer
proc secp256k1_schnorrsig_sign32*(ctx: ptr secp256k1_context;
sig64: ptr byte;
msg32: ptr byte;
keypair: ptr secp256k1_keypair;
aux_rand32: ptr byte): cint {.secp.}
## Create a Schnorr signature.
##
## Does _not_ strictly follow BIP-340 because it does not verify the resulting
## signature. Instead, you can manually use secp256k1_schnorrsig_verify and
## abort if it fails.
##
## This function only signs 32-byte messages. If you have messages of a
## different size (or the same size but without a context-specific tag
## prefix), it is recommended to create a 32-byte message hash with
## secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows
## providing an context-specific tag for domain separation. This prevents
## signatures from being valid in multiple contexts by accident.
##
## Returns 1 on success, 0 on failure.
## Args: ctx: pointer to a context object, initialized for signing.
## Out: sig64: pointer to a 64-byte array to store the serialized signature.
## In: msg32: the 32-byte message being signed.
## keypair: pointer to an initialized keypair.
## aux_rand32: 32 bytes of fresh randomness. While recommended to provide
## this, it is only supplemental to security and can be NULL. A
## NULL argument is treated the same as an all-zero one. See
## BIP-340 "Default Signing" for a full explanation of this
## argument and for guidance if randomness is expensive.
##
proc secp256k1_schnorrsig_sign_custom*(
ctx: ptr secp256k1_context;
sig64: ptr byte;
msg: ptr byte;
msglen: csize_t;
keypair: ptr secp256k1_keypair;
extraparams: ptr secp256k1_schnorrsig_extraparams): cint {.secp.}
## Create a Schnorr signature with a more flexible API.
##
## Same arguments as secp256k1_schnorrsig_sign except that it allows signing
## variable length messages and accepts a pointer to an extraparams object that
## allows customizing signing by passing additional arguments.
##
## Creates the same signatures as schnorrsig_sign if msglen is 32 and the
## extraparams.ndata is the same as aux_rand32.
##
## In: msg: the message being signed. Can only be NULL if msglen is 0.
## msglen: length of the message
## extraparams: pointer to a extraparams object (can be NULL)
##
proc secp256k1_schnorrsig_verify*(
ctx: ptr secp256k1_context;
sig64: ptr byte;
msg: ptr byte;
msglen: csize_t;
pubkey: ptr secp256k1_xonly_pubkey): cint {.secp.}
## Verify a Schnorr signature.
##
## Returns: 1: correct signature
## 0: incorrect signature
## Args: ctx: a secp256k1 context object, initialized for verification.
## In: sig64: pointer to the 64-byte signature to verify.
## msg: the message being verified. Can only be NULL if msglen is 0.
## msglen: length of the message
## pubkey: pointer to an x-only public key to verify with (cannot be NULL)
##
var secp256k1_nonce_function_bip340*: secp256k1_nonce_function_hardened

View File

@ -5,7 +5,7 @@ set -e
THIS_DIR=$(dirname "$0")
cd $THIS_DIR/secp256k1
./autogen.sh
./configure --enable-module-ecdh --enable-module-recovery --enable-experimental
./configure --enable-module-ecdh --enable-module-recovery --enable-module-extrakeys --enable-module-schnorrsig --enable-experimental
make src/ecmult_static_context.h
cd -

View File

@ -18,13 +18,13 @@
#define ENABLE_MODULE_ECDH 1
/* Define this symbol to enable the extrakeys module */
/* #undef ENABLE_MODULE_EXTRAKEYS */
#define ENABLE_MODULE_EXTRAKEYS 1
/* Define this symbol to enable the ECDSA pubkey recovery module */
#define ENABLE_MODULE_RECOVERY 1
/* Define this symbol to enable the schnorrsig module */
/* #undef ENABLE_MODULE_SCHNORRSIG */
#define ENABLE_MODULE_SCHNORRSIG 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1

View File

@ -15,6 +15,13 @@ const
1'u8, 0, 0, 0, 0, 0, 0, 0,
1'u8, 0, 0, 0, 0, 0, 0, 0,
])
msg2 = array[40, byte]([
0'u8, 0, 0, 0, 0, 0, 0, 0,
0'u8, 0, 0, 0, 0, 0, 0, 0,
0'u8, 0, 0, 0, 0, 0, 0, 0,
0'u8, 0, 0, 0, 0, 0, 0, 0,
0'u8, 0, 0, 0, 0, 0, 0, 0,
])
proc workingRng(data: var openArray[byte]): bool =
data[0] += 1
@ -34,6 +41,8 @@ suite "secp256k1":
SkPublicKey.fromRaw(pk.toRaw())[].toHex() == pk.toHex()
SkPublicKey.fromRaw(pk.toRawCompressed())[].toHex() == pk.toHex()
SkPublicKey.fromHex(pk.toHex())[].toHex() == pk.toHex()
SkXOnlyPublicKey.fromRaw(pk.toXOnly.toRaw())[].toHex() == pk.toXOnly.toHex()
SkXOnlyPublicKey.fromHex(pk.toXOnly.toHex())[].toHex() == pk.toXOnly.toHex()
SkSecretKey.random(brokenRng).isErr
test "Signatures":
@ -43,6 +52,9 @@ suite "secp256k1":
otherPk = SkSecretKey.random(workingRng)[].toPublicKey()
sig = sign(sk, msg0)
sig2 = signRecoverable(sk, msg0)
sig3 = signSchnorr(sk, msg0, workingRng)[]
sig4 = signSchnorr(sk, cast[array[SkMessageSize, byte]](msg0), workingRng)[]
sig5 = signSchnorr(sk, msg2, workingRng)[]
check:
verify(sig, msg0, pk)
@ -51,6 +63,9 @@ suite "secp256k1":
recover(sig2, msg0)[] == pk
recover(sig2, msg1)[] != pk
SkSignature.fromDer(sig.toDer())[].toHex() == sig.toHex()
verify(sig3, msg0, pk)
sig3 == sig4
verify(sig5, msg2, pk)
test "Message":
check:

View File

@ -30,3 +30,17 @@ suite "ABI tests":
addr aPublicKey,
cast[ptr byte](addr bSecretKey[0])) == 1
check(data1 == data2)
test "C-side keypairs should be unchanged when serialized":
var keypair: secp256k1_keypair
var secretKey: array[32, uint8]
var publicKey: secp256k1_xonly_pubkey
var parsed: array[32, byte]
var reflectedPublicKey: secp256k1_xonly_pubkey
secretKey[31] = 1'u8
let ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN or SECP256K1_CONTEXT_VERIFY)
check secp256k1_keypair_create(ctx, addr keypair, cast[ptr byte](addr secretKey[0])) == 1
check secp256k1_keypair_xonly_pub(ctx, addr publicKey, nil, addr keypair) == 1
check secp256k1_xonly_pubkey_serialize(ctx, addr parsed[0], addr publicKey) == 1
check secp256k1_xonly_pubkey_parse(ctx, addr reflectedPublicKey, addr parsed[0]) == 1
check publicKey == reflectedPublicKey