Skip to content

Commit 962ce23

Browse files
committed
modules: add ecdsa_sign_to_contract
Sign a message while comitting to some data at the same time by by adding `hash(k1*G, data)` to the rfc6979 deterministic nonce. Includes verification function to check that the signature commits to the data.
1 parent bfbf416 commit 962ce23

File tree

9 files changed

+535
-23
lines changed

9 files changed

+535
-23
lines changed

.travis.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ cache:
1111
- src/java/guava/
1212
env:
1313
global:
14-
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no
14+
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no ECDSA_SIGN_TO_CONTRACT=no EXPERIMENTAL=no JNI=no
1515
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
1616
matrix:
1717
- SCALAR=32bit RECOVERY=yes
18-
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
18+
- SCALAR=32bit FIELD=32bit ECDH=yes ECDSA_SIGN_TO_CONTRACT=yes EXPERIMENTAL=yes
19+
- SCALAR=32bit FIELD=32bit EXPERIMENTAL=yes
1920
- SCALAR=64bit
2021
- FIELD=64bit RECOVERY=yes
2122
- FIELD=64bit ENDOMORPHISM=yes
22-
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
23+
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes ECDSA_SIGN_TO_CONTRACT=yes EXPERIMENTAL=yes
2324
- FIELD=64bit ASM=x86_64
2425
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
2526
- FIELD=32bit ENDOMORPHISM=yes
@@ -67,4 +68,4 @@ before_script: ./autogen.sh
6768
script:
6869
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
6970
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
70-
- ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --with-ecmult-gen-precision=$ECMULTGENPRECISION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
71+
- ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --with-ecmult-gen-precision=$ECMULTGENPRECISION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-module-ecdsa-sign-to-contract=$ECDSA_SIGN_TO_CONTRACT --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD

Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,7 @@ endif
181181
if ENABLE_MODULE_RECOVERY
182182
include src/modules/recovery/Makefile.am.include
183183
endif
184+
185+
if ENABLE_MODULE_ECDSA_SIGN_TO_CONTRACT
186+
include src/modules/ecdsa_sign_to_contract/Makefile.am.include
187+
endif

configure.ac

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ AC_ARG_ENABLE(module_recovery,
134134
[enable_module_recovery=$enableval],
135135
[enable_module_recovery=no])
136136

137+
AC_ARG_ENABLE(module_ecdsa_sign_to_contract,
138+
AS_HELP_STRING([--enable-module-ecdsa-sign-to-contract],[enable ECDSA sign-to-contract module [default=no]]),
139+
[enable_module_ecdsa_sign_to_contract=$enableval],
140+
[enable_module_ecdsa_sign_to_contract=no])
141+
137142
AC_ARG_ENABLE(external_default_callbacks,
138143
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
139144
[use_external_default_callbacks=$enableval],
@@ -516,6 +521,10 @@ if test x"$enable_module_recovery" = x"yes"; then
516521
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
517522
fi
518523

524+
if test x"$enable_module_ecdsa_sign_to_contract" = x"yes"; then
525+
AC_DEFINE(ENABLE_MODULE_ECDSA_SIGN_TO_CONTRACT, 1, [Define this symbol to enable the ECDSA sign-to-contract module])
526+
fi
527+
519528
AC_C_BIGENDIAN()
520529

521530
if test x"$use_external_asm" = x"yes"; then
@@ -531,11 +540,15 @@ if test x"$enable_experimental" = x"yes"; then
531540
AC_MSG_NOTICE([WARNING: experimental build])
532541
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
533542
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
543+
AC_MSG_NOTICE([Building ECDSA sign-to-contract module: $enable_module_ecdsa_sign_to_contract])
534544
AC_MSG_NOTICE([******])
535545
else
536546
if test x"$enable_module_ecdh" = x"yes"; then
537547
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
538548
fi
549+
if test x"$enable_module_ecdsa_sign_to_contract" = x"yes"; then
550+
AC_MSG_ERROR([ECDA sign-to-contract module module is experimental. Use --enable-experimental to allow.])
551+
fi
539552
if test x"$set_asm" = x"arm"; then
540553
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
541554
fi
@@ -555,6 +568,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
555568
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
556569
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
557570
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
571+
AM_CONDITIONAL([ENABLE_MODULE_ECDSA_SIGN_TO_CONTRACT], [test x"$enable_module_ecdsa_sign_to_contract" = x"yes"])
558572
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" = x"yes"])
559573
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
560574
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
@@ -568,24 +582,25 @@ AC_OUTPUT
568582

569583
echo
570584
echo "Build Options:"
571-
echo " with endomorphism = $use_endomorphism"
572-
echo " with ecmult precomp = $set_precomp"
573-
echo " with external callbacks = $use_external_default_callbacks"
574-
echo " with jni = $use_jni"
575-
echo " with benchmarks = $use_benchmark"
576-
echo " with coverage = $enable_coverage"
577-
echo " module ecdh = $enable_module_ecdh"
578-
echo " module recovery = $enable_module_recovery"
585+
echo " with endomorphism = $use_endomorphism"
586+
echo " with ecmult precomp = $set_precomp"
587+
echo " with external callbacks = $use_external_default_callbacks"
588+
echo " with jni = $use_jni"
589+
echo " with benchmarks = $use_benchmark"
590+
echo " with coverage = $enable_coverage"
591+
echo " module ecdh = $enable_module_ecdh"
592+
echo " module recovery = $enable_module_recovery"
593+
echo " module ecdsa sign-to-contract = $enable_module_ecdsa_sign_to_contract"
579594
echo
580-
echo " asm = $set_asm"
581-
echo " bignum = $set_bignum"
582-
echo " field = $set_field"
583-
echo " scalar = $set_scalar"
584-
echo " ecmult window size = $set_ecmult_window"
585-
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
595+
echo " asm = $set_asm"
596+
echo " bignum = $set_bignum"
597+
echo " field = $set_field"
598+
echo " scalar = $set_scalar"
599+
echo " ecmult window size = $set_ecmult_window"
600+
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
586601
echo
587-
echo " CC = $CC"
588-
echo " CFLAGS = $CFLAGS"
589-
echo " CPPFLAGS = $CPPFLAGS"
590-
echo " LDFLAGS = $LDFLAGS"
602+
echo " CC = $CC"
603+
echo " CFLAGS = $CFLAGS"
604+
echo " CPPFLAGS = $CPPFLAGS"
605+
echo " LDFLAGS = $LDFLAGS"
591606
echo
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#ifndef SECP256K1_ECDSA_SIGN_TO_CONTRACT_H
2+
#define SECP256K1_ECDSA_SIGN_TO_CONTRACT_H
3+
4+
#include "secp256k1.h"
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
/** Same as secp256k1_ecdsa_sign, but s2c_data32 is committed to by adding `hash(R1, s2c_data32)` to
11+
* the nonce generated by noncefp, with `ndata=hash(s2c_data32, ndata)`.
12+
* Returns: 1: signature created
13+
* 0: the nonce generation function failed, or the private key was invalid.
14+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
15+
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
16+
* s2c_opening: pointer to an secp256k1_s2c_opening structure which can be
17+
* NULL but is required to be not NULL if this signature creates
18+
* a sign-to-contract commitment (i.e. the `s2c_data` argument
19+
* is not NULL).
20+
* In:
21+
* msg32: the 32-byte message hash being signed (cannot be NULL)
22+
* seckey: pointer to a 32-byte secret key (cannot be NULL)
23+
* s2c_data32: pointer to a 32-byte data to create an optional
24+
* sign-to-contract commitment to if not NULL (can be NULL).
25+
* noncefp: pointer to a nonce generation function.
26+
* If NULL, secp256k1_nonce_function_default is used.
27+
* Must be the default if s2c_data32 is not NULL.
28+
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
29+
*/
30+
SECP256K1_API int secp256k1_ecdsa_s2c_sign(
31+
const secp256k1_context* ctx,
32+
secp256k1_ecdsa_signature *sig,
33+
secp256k1_s2c_opening *s2c_opening,
34+
const unsigned char *msg32,
35+
const unsigned char *seckey,
36+
const unsigned char* s2c_data32,
37+
secp256k1_nonce_function noncefp,
38+
const void *ndata
39+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
40+
41+
/** Verify a sign-to-contract commitment.
42+
*
43+
* Returns: 1: the signature contains a commitment to data32
44+
* 0: incorrect opening
45+
* Args: ctx: a secp256k1 context object, initialized for verification.
46+
* In: sig: the signature containing the sign-to-contract commitment (cannot be NULL)
47+
* data32: the 32-byte data that was committed to (cannot be NULL)
48+
* opening: pointer to the opening created during signing (cannot be NULL)
49+
*/
50+
SECP256K1_API int secp256k1_ecdsa_s2c_verify_commit(
51+
const secp256k1_context* ctx,
52+
const secp256k1_ecdsa_signature *sig,
53+
const unsigned char *data32,
54+
const secp256k1_s2c_opening *opening
55+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
56+
57+
#ifdef __cplusplus
58+
}
59+
#endif
60+
61+
#endif /* SECP256K1_ECDSA_SIGN_TO_CONTRACT_H */
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include_HEADERS += include/secp256k1_ecdsa_sign_to_contract.h
2+
noinst_HEADERS += src/modules/ecdsa_sign_to_contract/main_impl.h
3+
noinst_HEADERS += src/modules/ecdsa_sign_to_contract/tests_impl.h
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**********************************************************************
2+
* Copyright (c) 2019 Marko Bencun, Jonas Nick *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
5+
**********************************************************************/
6+
7+
#ifndef SECP256K1_MODULE_ECDSA_SIGN_TO_CONTRACT_MAIN_H
8+
#define SECP256K1_MODULE_ECDSA_SIGN_TO_CONTRACT_MAIN_H
9+
10+
#include "include/secp256k1_ecdsa_sign_to_contract.h"
11+
12+
int secp256k1_ecdsa_s2c_sign(const secp256k1_context *ctx, secp256k1_ecdsa_signature *signature, secp256k1_s2c_opening *s2c_opening, const unsigned char *msg32, const unsigned char *seckey, const unsigned char* s2c_data32, secp256k1_nonce_function noncefp, const void* noncedata) {
13+
secp256k1_scalar r, s;
14+
secp256k1_scalar sec, non, msg;
15+
secp256k1_sha256 sha;
16+
int ret = 0;
17+
int overflow = 0;
18+
int is_zero = 0;
19+
unsigned char ndata[32];
20+
VERIFY_CHECK(ctx != NULL);
21+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
22+
ARG_CHECK(msg32 != NULL);
23+
ARG_CHECK(signature != NULL);
24+
ARG_CHECK(seckey != NULL);
25+
if (noncefp == NULL) {
26+
noncefp = secp256k1_nonce_function_default;
27+
}
28+
/* sign-to-contract commitments only work with the default nonce function,
29+
* because we need to ensure that s2c_data is actually hashed into the nonce and
30+
* not just ignored. */
31+
ARG_CHECK(s2c_data32 == NULL || noncefp == secp256k1_nonce_function_default);
32+
/* s2c_opening and s2c_data32 should be either both non-NULL or both NULL. */
33+
ARG_CHECK((s2c_opening != NULL) == (s2c_data32 != NULL));
34+
35+
if (s2c_opening != NULL) {
36+
secp256k1_s2c_opening_init(s2c_opening);
37+
}
38+
39+
if(s2c_data32 != NULL) {
40+
/* Provide s2c_data32 and ndata (if not NULL) to the the nonce function
41+
* as additional data to derive the nonce from. If both pointers are
42+
* not NULL, they need to be hashed to get the nonce data 32 bytes.
43+
* Even if only s2c_data32 is not NULL, it's hashed because it should
44+
* be possible to derive nonces even if only a SHA256 commitment to the
45+
* data is known. This is for example important in the
46+
* anti-nonce-sidechannel protocol.
47+
*/
48+
secp256k1_sha256_initialize(&sha);
49+
secp256k1_sha256_write(&sha, s2c_data32, 32);
50+
if (noncedata != NULL) {
51+
secp256k1_sha256_write(&sha, noncedata, 32);
52+
}
53+
secp256k1_sha256_finalize(&sha, ndata);
54+
noncedata = &ndata;
55+
}
56+
57+
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
58+
/* Fail if the secret key is invalid. */
59+
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
60+
unsigned char nonce32[32];
61+
unsigned int count = 0;
62+
secp256k1_scalar_set_b32(&msg, msg32, NULL);
63+
while (1) {
64+
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
65+
if (!ret) {
66+
break;
67+
}
68+
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
69+
is_zero = secp256k1_scalar_is_zero(&non);
70+
if (!overflow && !is_zero) {
71+
if (s2c_data32 != NULL) {
72+
secp256k1_gej nonce_pj;
73+
secp256k1_ge nonce_p;
74+
75+
/* Compute original nonce commitment/pubkey */
76+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_pj, &non);
77+
secp256k1_ge_set_gej(&nonce_p, &nonce_pj);
78+
secp256k1_pubkey_save(&s2c_opening->original_pubnonce, &nonce_p);
79+
80+
/* Tweak nonce with s2c commitment. */
81+
if (!secp256k1_ec_commit_seckey(ctx, nonce32, &s2c_opening->original_pubnonce, s2c_data32, 32)) {
82+
return 0;
83+
}
84+
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
85+
is_zero = secp256k1_scalar_is_zero(&non);
86+
}
87+
88+
if (!overflow && !is_zero) {
89+
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
90+
break;
91+
}
92+
}
93+
}
94+
count++;
95+
}
96+
memset(nonce32, 0, 32);
97+
secp256k1_scalar_clear(&msg);
98+
secp256k1_scalar_clear(&non);
99+
secp256k1_scalar_clear(&sec);
100+
}
101+
if (ret) {
102+
secp256k1_ecdsa_signature_save(signature, &r, &s);
103+
} else {
104+
memset(signature, 0, sizeof(*signature));
105+
}
106+
return ret;
107+
}
108+
109+
int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *data32, const secp256k1_s2c_opening *opening) {
110+
secp256k1_pubkey commitment;
111+
secp256k1_ge commitment_ge;
112+
unsigned char x_bytes1[32];
113+
unsigned char x_bytes2[32];
114+
secp256k1_scalar sigr, sigs;
115+
116+
VERIFY_CHECK(ctx != NULL);
117+
ARG_CHECK(sig != NULL);
118+
ARG_CHECK(data32 != NULL);
119+
ARG_CHECK(opening != NULL);
120+
ARG_CHECK(secp256k1_s2c_commit_is_init(opening));
121+
122+
if (!secp256k1_ec_commit(ctx, &commitment, &opening->original_pubnonce, data32, 32)) {
123+
return 0;
124+
}
125+
126+
/* Check that sigr (x coordinate of R) matches the x coordinate of the commitment. */
127+
secp256k1_ecdsa_signature_load(ctx, &sigr, &sigs, sig);
128+
129+
if (!secp256k1_pubkey_load(ctx, &commitment_ge, &commitment)) {
130+
return 0;
131+
}
132+
secp256k1_fe_normalize(&commitment_ge.x);
133+
secp256k1_fe_get_b32(x_bytes1, &commitment_ge.x);
134+
secp256k1_scalar_get_b32(x_bytes2, &sigr);
135+
return memcmp(x_bytes1, x_bytes2, 32) == 0;
136+
137+
}
138+
139+
#endif /* SECP256K1_ECDSA_SIGN_TO_CONTRACT_MAIN_H */

0 commit comments

Comments
 (0)