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:
parent
4c41c5029f
commit
6e18455a63
25
nimble.lock
25
nimble.lock
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
156
secp256k1.nim
156
secp256k1.nim
|
@ -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".}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 -
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue