Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow sign-to-contract commitments in schnorrsigs #588

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
bench_inv
bench_ecdh
bench_ecmult
bench_schnorrsig
bench_sign
bench_verify
bench_schnorr_verify
bench_recover
bench_internal
tests
Expand Down
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
endif

if ENABLE_MODULE_SCHNORRSIG
include src/modules/schnorrsig/Makefile.am.include
endif

if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
14 changes: 14 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ AC_ARG_ENABLE(module_ecdh,
[enable_module_ecdh=$enableval],
[enable_module_ecdh=no])

AC_ARG_ENABLE(module_schnorrsig,
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]),
[enable_module_schnorrsig=$enableval],
[enable_module_schnorrsig=no])

AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
[enable_module_recovery=$enableval],
Expand Down Expand Up @@ -431,6 +436,10 @@ if test x"$enable_module_ecdh" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
fi

if test x"$enable_module_schnorrsig" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module])
fi

if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi
Expand Down Expand Up @@ -458,11 +467,15 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_schnorrsig" = x"yes"; then
AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
fi
if test x"$set_asm" = x"arm"; then
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
fi
Expand All @@ -481,6 +494,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
Expand Down
56 changes: 56 additions & 0 deletions include/secp256k1.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,34 @@ typedef struct {
unsigned char data[64];
} secp256k1_ecdsa_signature;

/** Data structure that holds a sign-to-contract ("s2c") context. Sign-to-contract
* allows a signer to commit to some data as part of a signature. If the nonce
* function supports sign-to-contract, after creating it with
* secp256k1_s2c_commit_context_create the context can be given to a signing
* algorithm via the nonce data argument.
*
* This structure is not opaque, but it is strongly discouraged to read or write to
* it directly. Use the secp256k1_s2c_commit_* instead to access a sign-to-contract
* context.
*
* 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 136 bytes in size, and can be safely copied/moved.
*/
typedef struct {
/* magic is set during initialization. It allows functions casting to
* s2c_commit_contexts from a void pointer to check if they actually got an
* s2c_commit_context and if it has been initialized. */
unsigned char magic[8];
unsigned char data[32];
unsigned char data_hash[32];
secp256k1_pubkey original_pubnonce;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the "public nonce" really be represented as pubkey? for me ge or gej would be more readable, but they're probably not exported....
(I got confused while diving into secp256k1_ec_commit_seckey and secp256k1_ec_commit_tweak about the whole public key thing)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ge or gej are not exported. But, hm, perhaps this should be a pubnonce type if it already confuses people.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, yes. A public key is a public key, and a nonce is a different thing. (I mean we can't enforce anything in C but maybe it's helpful to use different names for the two public types at least.)

Do we have the same problem in other places too?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a second thought... if we really want to optimize for a simple API, then #589 is the way to go. There the entire problem is eliminated by hiding from the user that a signature contains a "nonce".

Copy link
Contributor

@elichai elichai Jul 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@real-or-random real-or-random Jul 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair

} secp256k1_s2c_commit_context;

/** A pointer to a function to deterministically generate a nonce.
*
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail.
* Args: ctx: an existing context object
* Out: nonce32: pointer to a 32-byte array to be filled by the function.
* In: msg32: the 32-byte message hash being verified (will not be NULL)
* key32: pointer to a 32-byte secret key (will not be NULL)
Expand All @@ -97,6 +122,7 @@ typedef struct {
* the message, the algorithm, the key and the attempt.
*/
typedef int (*secp256k1_nonce_function)(
const secp256k1_context *ctx,
unsigned char *nonce32,
const unsigned char *msg32,
const unsigned char *key32,
Expand Down Expand Up @@ -469,6 +495,36 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize(
const secp256k1_ecdsa_signature *sigin
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);

/** Creates a sign-to-contract context.
*
* Returns: 1 if the context was created successfully, 0 otherwise
* Args: ctx: a secp256k1 context object
* Out: s2c_ctx: pointer to an s2c context to initialize (cannot be NULL)
* In: data32: the 32-byte data to commit to (cannot be NULL)
*/
SECP256K1_API int secp256k1_s2c_commit_context_create(
secp256k1_context *ctx,
secp256k1_s2c_commit_context *s2c_ctx,
const unsigned char *data32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Gets the original nonce of a sign-to-contract commitment from an s2c_ctx after
* signing. The original nonce is the signature nonce minus the s2c commitment
* tweak. Together with the committed data this is the opening of the commitment.
*
* Returns: 1 if getting the original nonce was successful, 0 otherwise
* Args: ctx: a secp256k1 context object
* Out: original_nonce: pointer to a pubkey object where the original nonce will be
* placed (cannot be NULL)
* In: s2c_ctx: pointer to an s2c context to get the original nonce from
* after signing. (cannot be NULL)
*/
SECP256K1_API int secp256k1_s2c_commit_get_original_nonce(
secp256k1_context *ctx,
secp256k1_pubkey *original_nonce,
const secp256k1_s2c_commit_context *s2c_ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function.
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* extra entropy.
Expand Down
141 changes: 141 additions & 0 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#ifndef SECP256K1_SCHNORRSIG_H
#define SECP256K1_SCHNORRSIG_H

/** This module implements a variant of Schnorr signatures compliant with
* BIP-schnorr
* (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki).
*/

/** Opaque data structure that holds a parsed Schnorr signature.
*
* 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, or
* comparison, use the `secp256k1_schnorrsig_serialize` and
* `secp256k1_schnorrsig_parse` functions.
*/
typedef struct {
unsigned char data[64];
} secp256k1_schnorrsig;

/** Serialize a Schnorr signature.
*
* Returns: 1
* Args: ctx: a secp256k1 context object
* Out: out64: pointer to a 64-byte array to store the serialized signature
* In: sig: pointer to the signature
*
* See secp256k1_schnorrsig_parse for details about the encoding.
*/
SECP256K1_API int secp256k1_schnorrsig_serialize(
const secp256k1_context* ctx,
unsigned char *out64,
const secp256k1_schnorrsig* sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Parse a Schnorr signature.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: sig: pointer to a signature object
* In: in64: pointer to the 64-byte signature to be parsed
*
* The signature is serialized in the form R||s, where R is a 32-byte public
* key (x-coordinate only; the y-coordinate is considered to be the unique
* y-coordinate satisfying the curve equation that is a quadratic residue)
* and s is a 32-byte big-endian scalar.
*
* After the call, sig will always be initialized. If parsing failed or the
* encoded numbers are out of range, signature validation with it is
* guaranteed to fail for every message and public key.
*/
SECP256K1_API int secp256k1_schnorrsig_parse(
const secp256k1_context* ctx,
secp256k1_schnorrsig* sig,
const unsigned char *in64
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Create a Schnorr signature.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: sig: pointer to the returned signature (cannot be NULL)
* nonce_is_negated: a pointer to an integer indicates if signing algorithm negated the
* nonce (can be NULL)
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
* seckey: pointer to a 32-byte secret key (cannot be NULL)
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_bipschnorr is used
* ndata: pointer to arbitrary data used by the nonce generation function. If non-NULL must
* be a pointer to an s2c_context object when using the default nonce function
* secp256k1_nonce_function_bipschnorr. s2c_context must be initialized with
* secp256k1_s2c_commit_context_create. (can be NULL)
*/
SECP256K1_API int secp256k1_schnorrsig_sign(
const secp256k1_context* ctx,
secp256k1_schnorrsig *sig,
int *nonce_is_negated,
const unsigned char *msg32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Verify a Schnorr signature.
*
* Returns: 1: correct signature
* 0: incorrect or unparseable signature
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig: the signature being verified (cannot be NULL)
* msg32: the 32-byte message hash being verified (cannot be NULL)
* pubkey: pointer to a public key to verify with (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
const secp256k1_context* ctx,
const secp256k1_schnorrsig *sig,
const unsigned char *msg32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Verifies a set of Schnorr signatures.
*
* Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0.
*
* Args: ctx: a secp256k1 context object, initialized for verification.
* scratch: scratch space used for the multiexponentiation
* In: sig: array of signatures, or NULL if there are no signatures
* msg32: array of messages, or NULL if there are no signatures
* pk: array of public keys, or NULL if there are no signatures
* n_sigs: number of signatures in above arrays. Must be smaller than
* 2^31 and smaller than half the maximum size_t value. Must be 0
* if above arrays are NULL.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch(
const secp256k1_context* ctx,
secp256k1_scratch_space *scratch,
const secp256k1_schnorrsig *const *sig,
const unsigned char *const *msg32,
const secp256k1_pubkey *const *pk,
size_t n_sigs
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);

/** Verify a sign-to-contract commitment.
*
* Returns: 1: the signature contains a commitment to data32
* 0: incorrect opening
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig: the signature containing the sign-to-contract commitment (cannot be NULL)
* data32: the 32-byte data that was committed to (cannot be NULL)
* original_nonce: pointer to the original_nonce created when signing (cannot be NULL)
* negated_nonce: integer indicating if signing algorithm negated the nonce
*/
SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit(
const secp256k1_context* ctx,
const secp256k1_schnorrsig *sig,
const unsigned char *data32,
const secp256k1_pubkey *original_nonce,
int negated_nonce
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

#endif
Loading