Skip to content

Commit 673e551

Browse files
Merge #111: Add ECDSA sign-to-contract module
47efb5e ecdsa-s2c: add ctime tests (Andrew Poelstra) 396b558 ecdsa-s2c: add anti-klepto protocol (Andrew Poelstra) 290dee5 ecdsa-s2c: add actual sign-to-contract functionality (Andrew Poelstra) 8e46cac ecdsa-s2c: block in module (Andrew Poelstra) 826bd04 add eccommit functionality (Andrew Poelstra) Pull request description: This is a backport and rebase of bitcoin-core/secp256k1#669 ACKs for top commit: jonasnick: ACK 47efb5e real-or-random: ACK 47efb5e Tree-SHA512: e1f3ee3985bc77197eb57c03884b5d4a5f8733523bba50e11309f86388471c6265b7241e9856e1b80a88f4c268f2826c0394e26161292aa438b2246a1ad86aa1
2 parents 0129b77 + 47efb5e commit 673e551

15 files changed

+1146
-8
lines changed

.travis.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,19 @@ compiler:
1717
- gcc
1818
env:
1919
global:
20-
- WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no
20+
- WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no ECDSA_S2C=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no
2121
matrix:
2222
- WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes
2323
- WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes
2424
- WIDEMUL=int64 RECOVERY=yes
25-
- WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
25+
- WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes
2626
- WIDEMUL=int128
27-
- WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
28-
- WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
27+
- WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes
28+
- WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes
2929
- WIDEMUL=int128 ASM=x86_64
3030
- BIGNUM=no
3131
- BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
32+
- BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes
3233
- BIGNUM=no STATICPRECOMPUTATION=no
3334
- BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no
3435
- CPPFLAGS=-DDETERMINISTIC

Makefile.am

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ noinst_HEADERS += src/group.h
1616
noinst_HEADERS += src/group_impl.h
1717
noinst_HEADERS += src/num_gmp.h
1818
noinst_HEADERS += src/num_gmp_impl.h
19+
noinst_HEADERS += src/eccommit.h
20+
noinst_HEADERS += src/eccommit_impl.h
1921
noinst_HEADERS += src/ecdsa.h
2022
noinst_HEADERS += src/ecdsa_impl.h
2123
noinst_HEADERS += src/eckey.h
@@ -182,3 +184,8 @@ endif
182184
if ENABLE_MODULE_SCHNORRSIG
183185
include src/modules/schnorrsig/Makefile.am.include
184186
endif
187+
188+
if ENABLE_MODULE_ECDSA_S2C
189+
include src/modules/ecdsa_s2c/Makefile.am.include
190+
endif
191+

configure.ac

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ AC_ARG_ENABLE(module_schnorrsig,
161161
[enable_module_schnorrsig=$enableval],
162162
[enable_module_schnorrsig=no])
163163

164+
AC_ARG_ENABLE(module_ecdsa_s2c,
165+
AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]),
166+
[enable_module_ecdsa_s2c=$enableval],
167+
[enable_module_ecdsa_s2c=no])
168+
164169
AC_ARG_ENABLE(external_default_callbacks,
165170
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
166171
[use_external_default_callbacks=$enableval],
@@ -509,6 +514,10 @@ if test x"$enable_module_extrakeys" = x"yes"; then
509514
AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
510515
fi
511516

517+
if test x"$enable_module_ecdsa_s2c" = x"yes"; then
518+
AC_DEFINE(ENABLE_MODULE_ECDSA_S2C, 1, [Define this symbol to enable the ECDSA sign-to-contract module])
519+
fi
520+
512521
if test x"$use_external_asm" = x"yes"; then
513522
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
514523
fi
@@ -532,6 +541,7 @@ if test x"$enable_experimental" = x"yes"; then
532541
AC_MSG_NOTICE([Building MuSig module: $enable_module_musig])
533542
AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys])
534543
AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
544+
AC_MSG_NOTICE([Building ECDSA sign-to-contract module: $enable_module_ecdsa_s2c])
535545
AC_MSG_NOTICE([******])
536546

537547

@@ -565,6 +575,9 @@ else
565575
if test x"$enable_module_schnorrsig" = x"yes"; then
566576
AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
567577
fi
578+
if test x"$enable_module_ecdsa_s2c" = x"yes"; then
579+
AC_MSG_ERROR([ECDSA sign-to-contract module module is experimental. Use --enable-experimental to allow.])
580+
fi
568581
if test x"$set_asm" = x"arm"; then
569582
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
570583
fi
@@ -601,6 +614,7 @@ AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" =
601614
AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"])
602615
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
603616
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
617+
AM_CONDITIONAL([ENABLE_MODULE_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"])
604618
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
605619
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
606620
AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"])
@@ -625,6 +639,7 @@ echo " module ecdh = $enable_module_ecdh"
625639
echo " module recovery = $enable_module_recovery"
626640
echo " module extrakeys = $enable_module_extrakeys"
627641
echo " module schnorrsig = $enable_module_schnorrsig"
642+
echo " module ecdsa-s2c = $enable_module_ecdsa_s2c"
628643
echo
629644
echo " asm = $set_asm"
630645
echo " bignum = $set_bignum"

contrib/travis.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ fi
1717
--with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \
1818
--enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
1919
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
20+
--enable-module-ecdsa-s2c="$ECDSA_S2C" \
2021
--enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \
2122
--enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG"\
2223
--with-valgrind="$WITH_VALGRIND" \

include/secp256k1_ecdsa_s2c.h

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
#ifndef SECP256K1_ECDSA_S2C_H
2+
#define SECP256K1_ECDSA_S2C_H
3+
4+
#include "secp256k1.h"
5+
6+
/** This module implements the sign-to-contract scheme for ECDSA signatures, as
7+
* well as the "ECDSA Anti-Klepto Protocol" that is based on sign-to-contract
8+
* and is specified further down. The sign-to-contract scheme allows creating a
9+
* signature that also commits to some data. This works by offsetting the public
10+
* nonce point of the signature R by hash(R, data)*G where G is the secp256k1
11+
* group generator.
12+
*/
13+
14+
#ifdef __cplusplus
15+
extern "C" {
16+
#endif
17+
18+
/** Data structure that holds a sign-to-contract ("s2c") opening information.
19+
* Sign-to-contract allows a signer to commit to some data as part of a signature. It
20+
* can be used as an Out-argument in certain signing functions.
21+
*
22+
* The exact representation of data inside is implementation defined and not
23+
* guaranteed to be portable between different platforms or versions. It is
24+
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
25+
* If you need to convert to a format suitable for storage, transmission, or
26+
* comparison, use secp256k1_ecdsa_s2c_opening_serialize and secp256k1_ecdsa_s2c_opening_parse.
27+
*/
28+
typedef struct {
29+
unsigned char data[64];
30+
} secp256k1_ecdsa_s2c_opening;
31+
32+
/** Parse a sign-to-contract opening.
33+
*
34+
* Returns: 1 if the opening could be parsed
35+
* 0 if the opening could not be parsed
36+
* Args: ctx: a secp256k1 context object.
37+
* Out: opening: pointer to an opening object. If 1 is returned, it is set to a
38+
* parsed version of input. If not, its value is unspecified.
39+
* In: input33: pointer to 33-byte array with a serialized opening
40+
*
41+
*/
42+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_parse(
43+
const secp256k1_context* ctx,
44+
secp256k1_ecdsa_s2c_opening* opening,
45+
const unsigned char* input33
46+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
47+
48+
/** Serialize a sign-to-contract opening into a byte sequence.
49+
*
50+
* Returns: 1 if the opening was successfully serialized.
51+
* 0 if the opening could not be serialized
52+
* Args: ctx: a secp256k1 context object
53+
* Out: output33: pointer to a 33-byte array to place the serialized opening in
54+
* In: opening: a pointer to an initialized `secp256k1_ecdsa_s2c_opening`
55+
*/
56+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_serialize(
57+
const secp256k1_context* ctx,
58+
unsigned char* output33,
59+
const secp256k1_ecdsa_s2c_opening* opening
60+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
61+
62+
/** Same as secp256k1_ecdsa_sign, but s2c_data32 is committed to inside the nonce
63+
*
64+
* Returns: 1: signature created
65+
* 0: the nonce generation function failed, or the private key was invalid.
66+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
67+
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
68+
* s2c_opening: if non-NULL, pointer to an secp256k1_ecdsa_s2c_opening structure to populate
69+
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
70+
* seckey: pointer to a 32-byte secret key (cannot be NULL)
71+
* s2c_data32: pointer to a 32-byte data to commit to in the nonce (cannot be NULL)
72+
*/
73+
SECP256K1_API int secp256k1_ecdsa_s2c_sign(
74+
const secp256k1_context* ctx,
75+
secp256k1_ecdsa_signature* sig,
76+
secp256k1_ecdsa_s2c_opening* s2c_opening,
77+
const unsigned char* msg32,
78+
const unsigned char* seckey,
79+
const unsigned char* s2c_data32
80+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
81+
82+
/** Verify a sign-to-contract commitment.
83+
*
84+
* Returns: 1: the signature contains a commitment to data32 (though it does
85+
* not necessarily need to be a valid siganture!)
86+
* 0: incorrect opening
87+
* Args: ctx: a secp256k1 context object, initialized for verification.
88+
* In: sig: the signature containing the sign-to-contract commitment (cannot be NULL)
89+
* data32: the 32-byte data that was committed to (cannot be NULL)
90+
* opening: pointer to the opening created during signing (cannot be NULL)
91+
*/
92+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit(
93+
const secp256k1_context* ctx,
94+
const secp256k1_ecdsa_signature *sig,
95+
const unsigned char *data32,
96+
const secp256k1_ecdsa_s2c_opening *opening
97+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
98+
99+
100+
/** ECDSA Anti-Klepto Protocol
101+
*
102+
* The ecdsa_anti_klepto_* functions can be used to prevent a signing device from
103+
* exfiltrating the secret signing keys through biased signature nonces. The general
104+
* idea is that a host provides additional randomness to the signing device client
105+
* and the client commits to the randomness in the nonce using sign-to-contract.
106+
*
107+
* The following scheme is described by Stepan Snigirev here:
108+
* https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017655.html
109+
* and by Pieter Wuille (as "Scheme 6") here:
110+
* https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-March/017667.html
111+
*
112+
* In order to ensure the host cannot trick the signing device into revealing its
113+
* keys, or the signing device to bias the nonce despite the host's contributions,
114+
* the host and client must engage in a commit-reveal protocol as follows:
115+
* 1. The host draws randomness `rho` and computes a sha256 commitment to it using
116+
* `secp256k1_ecdsa_anti_klepto_host_commit`. It sends this to the signing device.
117+
* 2. The signing device computes a public nonce `R` using the host's commitment
118+
* as auxiliary randomness, using `secp256k1_ecdsa_anti_klepto_signer_commit`.
119+
* The signing device sends the resulting `R` to the host as a s2c_opening.
120+
*
121+
* If, at any point from this step onward, the hardware device fails, it is
122+
* okay to restart the protocol using **exactly the same `rho`** and checking
123+
* that the hardware device proposes **exactly the same** `R`. Otherwise, the
124+
* hardware device may be selectively aborting and thereby biasing the set of
125+
* nonces that are used in actual signatures.
126+
*
127+
* It takes many (>100) such aborts before there is a plausible attack, given
128+
* current knowledge in 2020. However such aborts accumulate even across a total
129+
* replacement of all relevant devices (but not across replacement of the actual
130+
* signing keys with new independently random ones).
131+
*
132+
* In case the hardware device cannot be made to sign with the given `rho`, `R`
133+
* pair, wallet authors should alert the user and present a very scary message
134+
* implying that if this happens more than even a few times, say 20 or more times
135+
* EVER, they should change hardware vendors and perhaps sweep their coins.
136+
*
137+
* 3. The host replies with `rho` generated in step 1.
138+
* 4. The device signs with `secp256k1_anti_klepto_sign`, using `rho` as `host_data32`,
139+
* and sends the signature to the host.
140+
* 5. The host verifies that the signature's public nonce matches the opening from
141+
* step 2 and its original randomness `rho`, using `secp256k1_anti_klepto_host_verify`.
142+
*
143+
* Rationale:
144+
* - The reason for having a host commitment is to allow the signing device to
145+
* deterministically derive a unique nonce even if the host restarts the protocol
146+
* using the same message and keys. Otherwise the signer might reuse the original
147+
* nonce in two iterations of the protocol with different `rho`, which leaks the
148+
* the secret key.
149+
* - The signer does not need to check that the host commitment matches the host's
150+
* claimed `rho`. Instead it re-derives the commitment (and its original `R`) from
151+
* the provided `rho`. If this differs from the original commitment, the result
152+
* will be an invalid `s2c_opening`, but since `R` was unique there is no risk to
153+
* the signer's secret keys. Because of this, the signing device does not need to
154+
* maintain any state about the progress of the protocol.
155+
*/
156+
157+
/** Create the initial host commitment to `rho`. Part of the ECDSA Anti-Klepto Protocol.
158+
*
159+
* Returns 1 on success, 0 on failure.
160+
* Args: ctx: pointer to a context object (cannot be NULL)
161+
* Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL)
162+
* In: rand32: the 32-byte randomness to commit to (cannot be NULL). It must come from
163+
* a cryptographically secure RNG. As per the protocol, this value must not
164+
* be revealed to the client until after the host has received the client
165+
* commitment.
166+
*/
167+
SECP256K1_API int secp256k1_ecdsa_anti_klepto_host_commit(
168+
const secp256k1_context* ctx,
169+
unsigned char* rand_commitment32,
170+
const unsigned char* rand32
171+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
172+
173+
/** Compute signer's original nonce. Part of the ECDSA Anti-Klepto Protocol.
174+
*
175+
* Returns 1 on success, 0 on failure.
176+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
177+
* Out: s2c_opening: pointer to an s2c_opening where the signer's public nonce will be
178+
* placed. (cannot be NULL)
179+
* In: msg32: the 32-byte message hash to be signed (cannot be NULL)
180+
* seckey32: the 32-byte secret key used for signing (cannot be NULL)
181+
* rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL)
182+
*/
183+
SECP256K1_API int secp256k1_ecdsa_anti_klepto_signer_commit(
184+
const secp256k1_context* ctx,
185+
secp256k1_ecdsa_s2c_opening* s2c_opening,
186+
const unsigned char* msg32,
187+
const unsigned char* seckey32,
188+
const unsigned char* rand_commitment32
189+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
190+
191+
/** Same as secp256k1_ecdsa_sign, but commits to host randomness in the nonce. Part of the
192+
* ECDSA Anti-Klepto Protocol.
193+
*
194+
* Returns: 1: signature created
195+
* 0: the nonce generation function failed, or the private key was invalid.
196+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
197+
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
198+
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
199+
* seckey: pointer to a 32-byte secret key (cannot be NULL)
200+
* host_data32: pointer to 32-byte host-provided randomness (cannot be NULL)
201+
*/
202+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_sign(
203+
const secp256k1_context* ctx,
204+
secp256k1_ecdsa_signature* sig,
205+
const unsigned char* msg32,
206+
const unsigned char* seckey,
207+
const unsigned char* host_data32
208+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
209+
210+
/** Verify a signature was correctly constructed using the ECDSA Anti-Klepto Protocol.
211+
*
212+
* Returns: 1: the signature is valid and contains a commitment to host_data32
213+
* 0: incorrect opening
214+
* Args: ctx: a secp256k1 context object, initialized for verification.
215+
* In: sig: the signature produced by the signer (cannot be NULL)
216+
* msghash32: the 32-byte message hash being verified (cannot be NULL)
217+
* pubkey: pointer to the signer's public key (cannot be NULL)
218+
* host_data32: the 32-byte data provided by the host (cannot be NULL)
219+
* opening: the s2c opening provided by the signer (cannot be NULL)
220+
*/
221+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_host_verify(
222+
const secp256k1_context* ctx,
223+
const secp256k1_ecdsa_signature *sig,
224+
const unsigned char *msg32,
225+
const secp256k1_pubkey *pubkey,
226+
const unsigned char *host_data32,
227+
const secp256k1_ecdsa_s2c_opening *opening
228+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
229+
230+
#ifdef __cplusplus
231+
}
232+
#endif
233+
234+
#endif /* SECP256K1_ECDSA_S2C_H */

0 commit comments

Comments
 (0)