Skip to content

Commit d8837e5

Browse files
committed
Add anti nonce sidechannel protocol for schnorrsigs using nonce_function_bipschnorr
1 parent 01dd66c commit d8837e5

File tree

3 files changed

+237
-1
lines changed

3 files changed

+237
-1
lines changed

include/secp256k1_schnorrsig.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,77 @@ SECP256K1_API int secp256k1_schnorrsig_parse(
5656
const unsigned char *in64
5757
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
5858

59+
/** Anti Nonce Sidechannel Protocol
60+
*
61+
* The next functions can be used to prevent a signing device from exfiltrating the secret signing
62+
* keys through biased signature nonces. The general idea is that a host provides additional
63+
* randomness to the signing device client and the client commits to the randomness in the nonce
64+
* using sign-to-contract.
65+
* In order to make the randomness unpredictable, the host and client must engage in a
66+
* commit-reveal protocol as follows:
67+
* 1. The host draws the randomness, commits to it with the anti_nonce_sidechan_host_commit
68+
* function and sends the commitment to the client.
69+
* 2. The client commits to its sign-to-contract opening (which is the nonce without the
70+
* sign-to-contract tweak) using the hosts commitment by calling the
71+
* secp256k1_schnorrsig_anti_nonce_sidechan_client_commit function. The client gets the opening
72+
* of the sign-to-contract commitment using secp256k1_s2c_commit_get_opening and sends it to the
73+
* host.
74+
* 3. The host replies with the randomness generated in step 1.
75+
* 4. The client uses anti_nonce_sidechan_client_setrand to check that the hosts commitment opens
76+
* to the provided randomness. If not, it waits until the host sends the correct randomness or
77+
* the protocol restarts. If the randomness matches the commitment, the client signs with the
78+
* nonce_function_bipschnorr using the s2c context as nonce data and sends the signature and
79+
* negated nonce flag to the host.
80+
* 5. The host checks that the signature contains an sign-to-contract commitment to the randomness
81+
* by calling verify_s2c_commit with the opening received in step 2 and the signature and
82+
* negated nonce flag received in step 4. If verification does not succeed, it waits until the
83+
* client sends a signature with a correct commitment or the protocol is restarted.
84+
*/
85+
86+
/** Create a randomness commitment on the host as part of the Anti Nonce Sidechannel Protocol.
87+
*
88+
* Returns 1 on success, 0 on failure.
89+
* Args: ctx: pointer to a context object (cannot be NULL)
90+
* Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL)
91+
* In: rand32: the 32-byte randomness to commit to (cannot be NULL)
92+
*/
93+
SECP256K1_API int secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(
94+
secp256k1_context *ctx,
95+
unsigned char *rand_commitment32,
96+
const unsigned char *rand32
97+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
98+
99+
/** Compute commitment on the client as part of the Anti Nonce Sidechannel Protocol.
100+
*
101+
* Returns 1 on success, 0 on failure.
102+
* Args: ctx: pointer to a context object (cannot be NULL)
103+
* Out: s2c_ctx: pointer to an s2c context where the opening will be placed (cannot be NULL)
104+
* In: msg32: the 32-byte message hash to be signed (cannot be NULL)
105+
* seckey32: the 32-byte secret key used for signing (cannot be NULL)
106+
* rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL)
107+
*/
108+
SECP256K1_API int secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(
109+
secp256k1_context *ctx,
110+
secp256k1_s2c_commit_context *s2c_ctx,
111+
const unsigned char *msg32,
112+
const unsigned char *seckey32,
113+
const unsigned char *rand_commitment32
114+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
115+
116+
/** Set host randomness on the client as part of the Anti Nonce Sidechannel Protocol.
117+
*
118+
* Returns: 1: given randomness matches randomness commitment stored in s2c_ctx
119+
* 0: failure
120+
* Args: ctx: pointer to a context object (cannot be NULL)
121+
* Out: s2c_ctx: pointer to an s2c context where the randomness will be stored (cannot be NULL)
122+
* In: rand32: 32-byte randomness matching the previously received commitment (cannot be NULL)
123+
*/
124+
SECP256K1_API int secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(
125+
secp256k1_context *ctx,
126+
secp256k1_s2c_commit_context *s2c_ctx,
127+
const unsigned char *rand32
128+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
129+
59130
/** Create a Schnorr signature.
60131
*
61132
* Returns 1 on success, 0 on failure.
@@ -117,4 +188,23 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch
117188
const secp256k1_pubkey *const *pk,
118189
size_t n_sigs
119190
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
191+
192+
/** Verify a sign-to-contract commitment.
193+
*
194+
* Returns: 1: the signature contains a commitment to data32
195+
* 0: incorrect opening
196+
* Args: ctx: a secp256k1 context object, initialized for verification.
197+
* In: sig: the signature containing the sign-to-contract commitment (cannot be NULL)
198+
* data32: the 32-byte data that was committed to (cannot be NULL)
199+
* opening: pointer to the opening created when signing (cannot be NULL)
200+
* negated_nonce: integer indicating if signing algorithm negated the nonce (can be NULL)
201+
*/
202+
SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit(
203+
const secp256k1_context* ctx,
204+
const secp256k1_schnorrsig *sig,
205+
const unsigned char *data32,
206+
const secp256k1_pubkey *opening,
207+
int negated_nonce
208+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
209+
120210
#endif

src/modules/schnorrsig/main_impl.h

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,73 @@ int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsi
2929
return 1;
3030
}
3131

32+
int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *data32, const secp256k1_pubkey *opening, int negated_nonce) {
33+
secp256k1_fe rx;
34+
secp256k1_ge R;
35+
secp256k1_pubkey pubnonce;
36+
37+
VERIFY_CHECK(ctx != NULL);
38+
ARG_CHECK(sig != NULL);
39+
ARG_CHECK(data32 != NULL);
40+
ARG_CHECK(opening != NULL);
41+
42+
if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) {
43+
return 0;
44+
}
45+
if (!secp256k1_ge_set_xquad(&R, &rx)) {
46+
return 0;
47+
}
48+
if(negated_nonce) {
49+
secp256k1_ge_neg(&R, &R);
50+
}
51+
secp256k1_pubkey_save(&pubnonce, &R);
52+
return secp256k1_ec_commit_verify(ctx, &pubnonce, opening, data32, 32);
53+
}
54+
55+
int secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(secp256k1_context *ctx, unsigned char *rand_commitment32, const unsigned char *rand32) {
56+
secp256k1_sha256 sha;
57+
58+
VERIFY_CHECK(ctx != NULL);
59+
ARG_CHECK(rand_commitment32 != NULL);
60+
ARG_CHECK(rand32 != NULL);
61+
62+
secp256k1_sha256_initialize(&sha);
63+
secp256k1_sha256_write(&sha, rand32, 32);
64+
secp256k1_sha256_finalize(&sha, rand_commitment32);
65+
66+
return 1;
67+
}
68+
69+
int secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(secp256k1_context *ctx, secp256k1_s2c_commit_context *s2c_ctx, const unsigned char *msg32, const unsigned char *seckey32, const unsigned char *rand_commitment32) {
70+
unsigned char nonce32[32];
71+
VERIFY_CHECK(ctx != NULL);
72+
ARG_CHECK(s2c_ctx != NULL);
73+
ARG_CHECK(msg32 != NULL);
74+
ARG_CHECK(seckey32 != NULL);
75+
ARG_CHECK(rand_commitment32 != NULL);
76+
77+
memcpy(s2c_ctx->data_commitment, rand_commitment32, 32);
78+
return secp256k1_nonce_function_bipschnorr_no_s2c(ctx, nonce32, msg32, seckey32, NULL, s2c_ctx, 0);
79+
}
80+
81+
int secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(secp256k1_context *ctx, secp256k1_s2c_commit_context *s2c_ctx, const unsigned char *rand32) {
82+
secp256k1_sha256 sha;
83+
unsigned char rand_hash[32];
84+
85+
VERIFY_CHECK(ctx != NULL);
86+
ARG_CHECK(s2c_ctx != NULL);
87+
ARG_CHECK(rand32 != NULL);
88+
89+
secp256k1_sha256_initialize(&sha);
90+
secp256k1_sha256_write(&sha, rand32, 32);
91+
secp256k1_sha256_finalize(&sha, rand_hash);
92+
if (memcmp(rand_hash, s2c_ctx->data_commitment, 32) != 0) {
93+
return 0;
94+
}
95+
memcpy(s2c_ctx->data, rand32, 32);
96+
return 1;
97+
}
98+
3299
int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, int *negated_nonce, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) {
33100
secp256k1_scalar x;
34101
secp256k1_scalar e;
@@ -334,5 +401,4 @@ int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_sc
334401
return secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &rj, &s, secp256k1_schnorrsig_verify_batch_ecmult_callback, (void *) &ecmult_context, 2 * n_sigs)
335402
&& secp256k1_gej_is_infinity(&rj);
336403
}
337-
338404
#endif

src/modules/schnorrsig/tests_impl.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) {
2525
unsigned char sk2[32];
2626
unsigned char sk3[32];
2727
unsigned char msg[32];
28+
unsigned char rand32[32];
29+
unsigned char rand_commitment32[32];
2830
unsigned char sig64[64];
2931
secp256k1_pubkey pk[3];
3032
secp256k1_schnorrsig sig;
33+
secp256k1_s2c_commit_context s2c_ctx;
3134
const secp256k1_schnorrsig *sigptr = &sig;
3235
const unsigned char *msgptr = msg;
3336
const secp256k1_pubkey *pkptr = &pk[0];
@@ -88,6 +91,50 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) {
8891
CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0);
8992
CHECK(ecount == 4);
9093

94+
secp256k1_rand256(rand32);
95+
ecount = 0;
96+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, rand_commitment32, rand32) == 1);
97+
CHECK(ecount == 0);
98+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, NULL, rand32) == 0);
99+
CHECK(ecount == 1);
100+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, rand_commitment32, NULL) == 0);
101+
CHECK(ecount == 2);
102+
103+
ecount = 0;
104+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, sk1, rand_commitment32) == 1);
105+
CHECK(ecount == 0);
106+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(none, &s2c_ctx, msg, sk1, rand_commitment32) == 0);
107+
CHECK(ecount == 1);
108+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, NULL, msg, sk1, rand_commitment32) == 0);
109+
CHECK(ecount == 2);
110+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, NULL, sk1, rand_commitment32) == 0);
111+
CHECK(ecount == 3);
112+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, NULL, rand_commitment32) == 0);
113+
CHECK(ecount == 4);
114+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, sk1, NULL) == 0);
115+
CHECK(ecount == 5);
116+
117+
ecount = 0;
118+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, &s2c_ctx, rand32) == 1);
119+
CHECK(ecount == 0);
120+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, NULL, rand32) == 0);
121+
CHECK(ecount == 1);
122+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, &s2c_ctx, NULL) == 0);
123+
CHECK(ecount == 2);
124+
125+
CHECK(secp256k1_schnorrsig_sign(sign, &sig, &negated_nonce, msg, sk1, NULL, &s2c_ctx) == 1);
126+
ecount = 0;
127+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(none, &sig, rand32, &s2c_ctx.original_pubnonce, negated_nonce) == 0);
128+
CHECK(ecount == 1);
129+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, rand32, &s2c_ctx.original_pubnonce, negated_nonce) == 1);
130+
CHECK(ecount == 1);
131+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, NULL, rand32, &s2c_ctx.original_pubnonce, negated_nonce) == 0);
132+
CHECK(ecount == 2);
133+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, NULL, &s2c_ctx.original_pubnonce, negated_nonce) == 0);
134+
CHECK(ecount == 3);
135+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, rand32, NULL, negated_nonce) == 0);
136+
CHECK(ecount == 4);
137+
91138
ecount = 0;
92139
CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0);
93140
CHECK(ecount == 1);
@@ -656,6 +703,38 @@ void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) {
656703
}
657704
#undef N_SIGS
658705

706+
void test_schnorrsig_anti_nonce_sidechannel(void) {
707+
unsigned char msg32[32];
708+
unsigned char key32[32];
709+
unsigned char algo16[16];
710+
unsigned char rand32[32];
711+
unsigned char rand_commitment32[32];
712+
secp256k1_s2c_commit_context s2c_ctx;
713+
secp256k1_pubkey s2c_opening;
714+
secp256k1_schnorrsig sig;
715+
int negated_nonce;
716+
717+
secp256k1_rand256(msg32);
718+
secp256k1_rand256(key32);
719+
secp256k1_rand256(rand32);
720+
memset(algo16, 23, sizeof(algo16));
721+
722+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(ctx, rand_commitment32, rand32) == 1);
723+
724+
/* Host sends rand_commitment32 to client. */
725+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(ctx, &s2c_ctx, msg32, key32, rand_commitment32) == 1);
726+
727+
/* Client sends s2c opening. Host replies with rand32. */
728+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(ctx, &s2c_ctx, rand32) == 1);
729+
/* Providing wrong data results in an error. */
730+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(ctx, &s2c_ctx, rand_commitment32) == 0);
731+
CHECK(secp256k1_s2c_commit_get_opening(ctx, &s2c_opening, &s2c_ctx) == 1);
732+
CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &negated_nonce, msg32, key32, NULL, &s2c_ctx) == 1);
733+
734+
/* Client sends signature to host. */
735+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, rand32, &s2c_opening, negated_nonce) == 1);
736+
}
737+
659738
void run_schnorrsig_tests(void) {
660739
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);
661740

@@ -664,6 +743,7 @@ void run_schnorrsig_tests(void) {
664743
test_schnorrsig_bip_vectors(scratch);
665744
test_schnorrsig_sign();
666745
test_schnorrsig_sign_verify(scratch);
746+
test_schnorrsig_anti_nonce_sidechannel();
667747

668748
secp256k1_scratch_space_destroy(scratch);
669749
}

0 commit comments

Comments
 (0)