Skip to content

Commit bcfc256

Browse files
committed
Add anti nonce sidechannel protocol for schnorrsigs using nonce_function_bipschnorr
1 parent 30c1d71 commit bcfc256

File tree

3 files changed

+184
-0
lines changed

3 files changed

+184
-0
lines changed

include/secp256k1_schnorrsig.h

Lines changed: 71 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 original nonce (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 original
72+
* nonce of the sign-to-contract commitment using secp256k1_s2c_commit_get_original_nonce and
73+
* sends it to the 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 original nonce 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.

src/modules/schnorrsig/main_impl.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,50 @@ int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const s
5252
return secp256k1_ec_commit_verify(ctx, &pubnonce, original_nonce, data32, 32);
5353
}
5454

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_hash, rand_commitment32, 32);
78+
return secp256k1_nonce_function_bipschnorr_no_s2c_tweak(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_hash, 32) != 0) {
93+
return 0;
94+
}
95+
memcpy(s2c_ctx->data, rand32, 32);
96+
return 1;
97+
}
98+
5599
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) {
56100
secp256k1_scalar x;
57101
secp256k1_scalar e;

src/modules/schnorrsig/tests_impl.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) {
2626
unsigned char sk3[32];
2727
unsigned char msg[32];
2828
unsigned char data32[32];
29+
unsigned char rand32[32];
30+
unsigned char rand_commitment32[32];
2931
unsigned char sig64[64];
3032
secp256k1_pubkey pk[3];
3133
secp256k1_schnorrsig sig;
@@ -116,6 +118,39 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) {
116118
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, data32, NULL, nonce_is_negated) == 0);
117119
CHECK(ecount == 4);
118120

121+
secp256k1_rand256(rand32);
122+
ecount = 0;
123+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, rand_commitment32, rand32) == 1);
124+
CHECK(ecount == 0);
125+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, NULL, rand32) == 0);
126+
CHECK(ecount == 1);
127+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, rand_commitment32, NULL) == 0);
128+
CHECK(ecount == 2);
129+
130+
ecount = 0;
131+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, sk1, rand_commitment32) == 1);
132+
CHECK(ecount == 0);
133+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(none, &s2c_ctx, msg, sk1, rand_commitment32) == 0);
134+
CHECK(ecount == 1);
135+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, NULL, msg, sk1, rand_commitment32) == 0);
136+
CHECK(ecount == 2);
137+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, NULL, sk1, rand_commitment32) == 0);
138+
CHECK(ecount == 3);
139+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, NULL, rand_commitment32) == 0);
140+
CHECK(ecount == 4);
141+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, sk1, NULL) == 0);
142+
CHECK(ecount == 5);
143+
144+
ecount = 0;
145+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, &s2c_ctx, rand32) == 1);
146+
CHECK(ecount == 0);
147+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, NULL, rand32) == 0);
148+
CHECK(ecount == 1);
149+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, &s2c_ctx, NULL) == 0);
150+
CHECK(ecount == 2);
151+
CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, sk1, NULL, &s2c_ctx) == 1);
152+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, rand32, &s2c_ctx.original_pubnonce, nonce_is_negated) == 1);
153+
119154
ecount = 0;
120155
CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0);
121156
CHECK(ecount == 1);
@@ -780,6 +815,38 @@ void test_schnorrsig_s2c_commit_verify(void) {
780815
}
781816
}
782817

818+
void test_schnorrsig_anti_nonce_sidechannel(void) {
819+
unsigned char msg32[32];
820+
unsigned char key32[32];
821+
unsigned char algo16[16];
822+
unsigned char rand32[32];
823+
unsigned char rand_commitment32[32];
824+
secp256k1_s2c_commit_context s2c_ctx;
825+
secp256k1_pubkey s2c_original_nonce;
826+
secp256k1_schnorrsig sig;
827+
int nonce_is_negated;
828+
829+
secp256k1_rand256(msg32);
830+
secp256k1_rand256(key32);
831+
secp256k1_rand256(rand32);
832+
memset(algo16, 23, sizeof(algo16));
833+
834+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(ctx, rand_commitment32, rand32) == 1);
835+
836+
/* Host sends rand_commitment32 to client. */
837+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(ctx, &s2c_ctx, msg32, key32, rand_commitment32) == 1);
838+
839+
/* Client sends s2c original nonce. Host replies with rand32. */
840+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(ctx, &s2c_ctx, rand32) == 1);
841+
/* Providing wrong data results in an error. */
842+
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(ctx, &s2c_ctx, rand_commitment32) == 0);
843+
CHECK(secp256k1_s2c_commit_get_original_nonce(ctx, &s2c_original_nonce, &s2c_ctx) == 1);
844+
CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &nonce_is_negated, msg32, key32, NULL, &s2c_ctx) == 1);
845+
846+
/* Client sends signature to host. */
847+
CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, rand32, &s2c_original_nonce, nonce_is_negated) == 1);
848+
}
849+
783850
void run_schnorrsig_tests(void) {
784851
int i;
785852
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);
@@ -794,6 +861,8 @@ void run_schnorrsig_tests(void) {
794861
* a test. */
795862
test_schnorrsig_s2c_commit_verify();
796863
}
864+
test_schnorrsig_anti_nonce_sidechannel();
865+
797866
secp256k1_scratch_space_destroy(scratch);
798867
}
799868

0 commit comments

Comments
 (0)