Skip to content

Commit

Permalink
Add anti nonce sidechannel protocol for schnorrsigs using nonce_funct…
Browse files Browse the repository at this point in the history
…ion_bipschnorr
  • Loading branch information
jonasnick committed Nov 1, 2018
1 parent 01dd66c commit d8837e5
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 1 deletion.
90 changes: 90 additions & 0 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,77 @@ SECP256K1_API int secp256k1_schnorrsig_parse(
const unsigned char *in64
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Anti Nonce Sidechannel Protocol
*
* The next functions can be used to prevent a signing device from exfiltrating the secret signing
* keys through biased signature nonces. The general idea is that a host provides additional
* randomness to the signing device client and the client commits to the randomness in the nonce
* using sign-to-contract.
* In order to make the randomness unpredictable, the host and client must engage in a
* commit-reveal protocol as follows:
* 1. The host draws the randomness, commits to it with the anti_nonce_sidechan_host_commit
* function and sends the commitment to the client.
* 2. The client commits to its sign-to-contract opening (which is the nonce without the
* sign-to-contract tweak) using the hosts commitment by calling the
* secp256k1_schnorrsig_anti_nonce_sidechan_client_commit function. The client gets the opening
* of the sign-to-contract commitment using secp256k1_s2c_commit_get_opening and sends it to the
* host.
* 3. The host replies with the randomness generated in step 1.
* 4. The client uses anti_nonce_sidechan_client_setrand to check that the hosts commitment opens
* to the provided randomness. If not, it waits until the host sends the correct randomness or
* the protocol restarts. If the randomness matches the commitment, the client signs with the
* nonce_function_bipschnorr using the s2c context as nonce data and sends the signature and
* negated nonce flag to the host.
* 5. The host checks that the signature contains an sign-to-contract commitment to the randomness
* by calling verify_s2c_commit with the opening received in step 2 and the signature and
* negated nonce flag received in step 4. If verification does not succeed, it waits until the
* client sends a signature with a correct commitment or the protocol is restarted.
*/

/** Create a randomness commitment on the host as part of the Anti Nonce Sidechannel Protocol.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL)
* In: rand32: the 32-byte randomness to commit to (cannot be NULL)
*/
SECP256K1_API int secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(
secp256k1_context *ctx,
unsigned char *rand_commitment32,
const unsigned char *rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Compute commitment on the client as part of the Anti Nonce Sidechannel Protocol.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: s2c_ctx: pointer to an s2c context where the opening will be placed (cannot be NULL)
* In: msg32: the 32-byte message hash to be signed (cannot be NULL)
* seckey32: the 32-byte secret key used for signing (cannot be NULL)
* rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL)
*/
SECP256K1_API 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
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Set host randomness on the client as part of the Anti Nonce Sidechannel Protocol.
*
* Returns: 1: given randomness matches randomness commitment stored in s2c_ctx
* 0: failure
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: s2c_ctx: pointer to an s2c context where the randomness will be stored (cannot be NULL)
* In: rand32: 32-byte randomness matching the previously received commitment (cannot be NULL)
*/
SECP256K1_API int secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(
secp256k1_context *ctx,
secp256k1_s2c_commit_context *s2c_ctx,
const unsigned char *rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Create a Schnorr signature.
*
* Returns 1 on success, 0 on failure.
Expand Down Expand Up @@ -117,4 +188,23 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch
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)
* opening: pointer to the opening created when signing (cannot be NULL)
* negated_nonce: integer indicating if signing algorithm negated the nonce (can be NULL)
*/
SECP256K1_API 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
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

#endif
68 changes: 67 additions & 1 deletion src/modules/schnorrsig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,73 @@ int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsi
return 1;
}

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) {
secp256k1_fe rx;
secp256k1_ge R;
secp256k1_pubkey pubnonce;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(data32 != NULL);
ARG_CHECK(opening != NULL);

if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) {
return 0;
}
if (!secp256k1_ge_set_xquad(&R, &rx)) {
return 0;
}
if(negated_nonce) {
secp256k1_ge_neg(&R, &R);
}
secp256k1_pubkey_save(&pubnonce, &R);
return secp256k1_ec_commit_verify(ctx, &pubnonce, opening, data32, 32);
}

int secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(secp256k1_context *ctx, unsigned char *rand_commitment32, const unsigned char *rand32) {
secp256k1_sha256 sha;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(rand_commitment32 != NULL);
ARG_CHECK(rand32 != NULL);

secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, rand32, 32);
secp256k1_sha256_finalize(&sha, rand_commitment32);

return 1;
}

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) {
unsigned char nonce32[32];
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(s2c_ctx != NULL);
ARG_CHECK(msg32 != NULL);
ARG_CHECK(seckey32 != NULL);
ARG_CHECK(rand_commitment32 != NULL);

memcpy(s2c_ctx->data_commitment, rand_commitment32, 32);
return secp256k1_nonce_function_bipschnorr_no_s2c(ctx, nonce32, msg32, seckey32, NULL, s2c_ctx, 0);
}

int secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(secp256k1_context *ctx, secp256k1_s2c_commit_context *s2c_ctx, const unsigned char *rand32) {
secp256k1_sha256 sha;
unsigned char rand_hash[32];

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(s2c_ctx != NULL);
ARG_CHECK(rand32 != NULL);

secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, rand32, 32);
secp256k1_sha256_finalize(&sha, rand_hash);
if (memcmp(rand_hash, s2c_ctx->data_commitment, 32) != 0) {
return 0;
}
memcpy(s2c_ctx->data, rand32, 32);
return 1;
}

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) {
secp256k1_scalar x;
secp256k1_scalar e;
Expand Down Expand Up @@ -334,5 +401,4 @@ int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_sc
return secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &rj, &s, secp256k1_schnorrsig_verify_batch_ecmult_callback, (void *) &ecmult_context, 2 * n_sigs)
&& secp256k1_gej_is_infinity(&rj);
}

#endif
80 changes: 80 additions & 0 deletions src/modules/schnorrsig/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) {
unsigned char sk2[32];
unsigned char sk3[32];
unsigned char msg[32];
unsigned char rand32[32];
unsigned char rand_commitment32[32];
unsigned char sig64[64];
secp256k1_pubkey pk[3];
secp256k1_schnorrsig sig;
secp256k1_s2c_commit_context s2c_ctx;
const secp256k1_schnorrsig *sigptr = &sig;
const unsigned char *msgptr = msg;
const secp256k1_pubkey *pkptr = &pk[0];
Expand Down Expand Up @@ -88,6 +91,50 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0);
CHECK(ecount == 4);

secp256k1_rand256(rand32);
ecount = 0;
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, rand_commitment32, rand32) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, NULL, rand32) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(none, rand_commitment32, NULL) == 0);
CHECK(ecount == 2);

ecount = 0;
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, sk1, rand_commitment32) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(none, &s2c_ctx, msg, sk1, rand_commitment32) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, NULL, msg, sk1, rand_commitment32) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, NULL, sk1, rand_commitment32) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, NULL, rand_commitment32) == 0);
CHECK(ecount == 4);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(sign, &s2c_ctx, msg, sk1, NULL) == 0);
CHECK(ecount == 5);

ecount = 0;
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, &s2c_ctx, rand32) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, NULL, rand32) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(none, &s2c_ctx, NULL) == 0);
CHECK(ecount == 2);

CHECK(secp256k1_schnorrsig_sign(sign, &sig, &negated_nonce, msg, sk1, NULL, &s2c_ctx) == 1);
ecount = 0;
CHECK(secp256k1_schnorrsig_verify_s2c_commit(none, &sig, rand32, &s2c_ctx.original_pubnonce, negated_nonce) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, rand32, &s2c_ctx.original_pubnonce, negated_nonce) == 1);
CHECK(ecount == 1);
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, NULL, rand32, &s2c_ctx.original_pubnonce, negated_nonce) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, NULL, &s2c_ctx.original_pubnonce, negated_nonce) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, rand32, NULL, negated_nonce) == 0);
CHECK(ecount == 4);

ecount = 0;
CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0);
CHECK(ecount == 1);
Expand Down Expand Up @@ -656,6 +703,38 @@ void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) {
}
#undef N_SIGS

void test_schnorrsig_anti_nonce_sidechannel(void) {
unsigned char msg32[32];
unsigned char key32[32];
unsigned char algo16[16];
unsigned char rand32[32];
unsigned char rand_commitment32[32];
secp256k1_s2c_commit_context s2c_ctx;
secp256k1_pubkey s2c_opening;
secp256k1_schnorrsig sig;
int negated_nonce;

secp256k1_rand256(msg32);
secp256k1_rand256(key32);
secp256k1_rand256(rand32);
memset(algo16, 23, sizeof(algo16));

CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_host_commit(ctx, rand_commitment32, rand32) == 1);

/* Host sends rand_commitment32 to client. */
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_commit(ctx, &s2c_ctx, msg32, key32, rand_commitment32) == 1);

/* Client sends s2c opening. Host replies with rand32. */
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(ctx, &s2c_ctx, rand32) == 1);
/* Providing wrong data results in an error. */
CHECK(secp256k1_schnorrsig_anti_nonce_sidechan_client_setrand(ctx, &s2c_ctx, rand_commitment32) == 0);
CHECK(secp256k1_s2c_commit_get_opening(ctx, &s2c_opening, &s2c_ctx) == 1);
CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &negated_nonce, msg32, key32, NULL, &s2c_ctx) == 1);

/* Client sends signature to host. */
CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, rand32, &s2c_opening, negated_nonce) == 1);
}

void run_schnorrsig_tests(void) {
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);

Expand All @@ -664,6 +743,7 @@ void run_schnorrsig_tests(void) {
test_schnorrsig_bip_vectors(scratch);
test_schnorrsig_sign();
test_schnorrsig_sign_verify(scratch);
test_schnorrsig_anti_nonce_sidechannel();

secp256k1_scratch_space_destroy(scratch);
}
Expand Down

0 comments on commit d8837e5

Please sign in to comment.