Skip to content

Commit ba34c6e

Browse files
committed
[experiment/wip] ecdsa nonce anti sidechan util functions
This is based on the description of the fix by Stepan: https://medium.com/cryptoadvance/hardware-wallets-can-be-hacked-but-this-is-fine-a6156bbd199 The protocol wording and functions are copied/adapted from Jonas Nick's PRs which do the same for BIP-Schnorr: ae5fb7f#diff-b19c5ee427283d4d82bc5beb4e2f4777R59 ae5fb7f#diff-313ca26f0048bc16a608709915d0111eR70 1. Add secp256k1_ecdsa_anti_nonce_sidechan_client_commit to return the curve point committing to the signing client nonce. This is a convenience function and can technically be emulated by calling secp256k1_ecdsa_sign() and reconstructing the curve point from the signature r/s values. 2. secp256k1_ecdsa_sign_nonce_tweak_add, which is the same as secp256k1_ecdsa_sign_nonce, but with an additional optional tweak parameter to add to the nonce. The nicer way to do this is to redefine `secp256k1_nonce_function` to have a tweak param, but this would break API compatiblity. The way it is implemented is fully backwards compatible.
1 parent fa33017 commit ba34c6e

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

include/secp256k1.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,37 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc
526526
/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */
527527
SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default;
528528

529+
/** Compute commitment on the client as part of the ECDSA Anti Nonce Sidechannel Protocol.
530+
*
531+
* ECDSA Anti Nonce Sidechannel Protocol:
532+
* 1. The host draws randomness `k2`, commits to it with sha256 and sends the commitment to the client.
533+
* 2. The client commits to it's original nonce `k1` using the host commitment by calling
534+
* `secp256k1_ecdsa_anti_nonce_sidechan_client_commit`. The client sends the resulting commitment
535+
* `R1` to the host.
536+
* 3. The host replies with `k2` generated in step 1.
537+
* 4. The client checks that the host commitment from step 1 commits to `k2` from step 3 and signs
538+
* with `secp256k1_ecdsa_sign_nonce_tweak_add`, using the `k2` as the tweak and the host
539+
* commitment as additional noncedata and sends the signature to the host.
540+
* 5. The host reconstructs the curve point `R` from the signature and verifies that `R = R1 + k2*G`.
541+
*
542+
* Returns 1 on success, 0 on failure.
543+
* Args: ctx: pointer to a context object (cannot be NULL)
544+
* Out: client_commit: pointer to a pubkey where the clients public nonce will be
545+
* placed. (cannot be NULL)
546+
* In: msg32: the 32-byte message hash to be signed (cannot be NULL)
547+
* seckey32: the 32-byte secret key used for signing (cannot be NULL)
548+
* noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
549+
* rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL)
550+
*/
551+
SECP256K1_API int secp256k1_ecdsa_anti_nonce_sidechan_client_commit(
552+
const secp256k1_context* ctx,
553+
secp256k1_pubkey *client_commit,
554+
const unsigned char *msg32,
555+
const unsigned char *seckey32,
556+
secp256k1_nonce_function noncefp,
557+
unsigned char *rand_commitment32
558+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6);
559+
529560
/** Create an ECDSA signature.
530561
*
531562
* Returns: 1: signature created
@@ -549,6 +580,18 @@ SECP256K1_API int secp256k1_ecdsa_sign(
549580
const void *ndata
550581
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
551582

583+
/** Same as secp256k1_ecdsa_sign, but nonce_tweak32 is added to the nonce generated by noncefp.
584+
*/
585+
SECP256K1_API int secp256k1_ecdsa_sign_nonce_tweak_add(
586+
const secp256k1_context* ctx,
587+
secp256k1_ecdsa_signature *sig,
588+
const unsigned char *msg32,
589+
const unsigned char *seckey,
590+
secp256k1_nonce_function noncefp,
591+
const void *ndata,
592+
const unsigned char* nonce_tweak32
593+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
594+
552595
/** Verify an ECDSA secret key.
553596
*
554597
* Returns: 1: secret key is valid

src/secp256k1.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,52 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
446446
const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979;
447447
const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979;
448448

449+
int secp256k1_ecdsa_anti_nonce_sidechan_client_commit(
450+
const secp256k1_context* ctx,
451+
secp256k1_pubkey *client_commit,
452+
const unsigned char *msg32,
453+
const unsigned char *seckey32,
454+
secp256k1_nonce_function noncefp,
455+
unsigned char *rand_commitment32
456+
) {
457+
unsigned char nonce32[32];
458+
secp256k1_scalar k;
459+
secp256k1_gej rj;
460+
secp256k1_ge r;
461+
462+
VERIFY_CHECK(ctx != NULL);
463+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
464+
ARG_CHECK(client_commit != NULL);
465+
ARG_CHECK(msg32 != NULL);
466+
ARG_CHECK(seckey32 != NULL);
467+
if (noncefp == NULL) {
468+
noncefp = secp256k1_nonce_function_default;
469+
}
470+
ARG_CHECK(rand_commitment32 != NULL);
471+
472+
473+
if (!noncefp(nonce32, msg32, seckey32, NULL, rand_commitment32, 0)) {
474+
return 0;
475+
}
476+
477+
secp256k1_scalar_set_b32(&k, nonce32, NULL);
478+
if (secp256k1_scalar_is_zero(&k)) {
479+
return 0;
480+
}
481+
482+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
483+
secp256k1_ge_set_gej(&r, &rj);
484+
secp256k1_pubkey_save(client_commit, &r);
485+
return 1;
486+
}
487+
449488
int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
489+
return secp256k1_ecdsa_sign_nonce_tweak_add(ctx, signature, msg32, seckey, noncefp, noncedata, NULL);
490+
}
491+
492+
int secp256k1_ecdsa_sign_nonce_tweak_add(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata, const unsigned char* nonce_tweak32) {
450493
secp256k1_scalar r, s;
451-
secp256k1_scalar sec, non, msg;
494+
secp256k1_scalar sec, non, msg, nonce_tweak;
452495
int ret = 0;
453496
int overflow = 0;
454497
VERIFY_CHECK(ctx != NULL);
@@ -460,6 +503,13 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
460503
noncefp = secp256k1_nonce_function_default;
461504
}
462505

506+
if (nonce_tweak32 != NULL) {
507+
secp256k1_scalar_set_b32(&nonce_tweak, nonce_tweak32, &overflow);
508+
if (overflow || secp256k1_scalar_is_zero(&nonce_tweak)) {
509+
return 0;
510+
}
511+
}
512+
463513
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
464514
/* Fail if the secret key is invalid. */
465515
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
@@ -472,6 +522,9 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
472522
break;
473523
}
474524
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
525+
if (nonce_tweak32 != NULL) {
526+
secp256k1_scalar_add(&non, &non, &nonce_tweak);
527+
}
475528
if (!overflow && !secp256k1_scalar_is_zero(&non)) {
476529
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
477530
break;

0 commit comments

Comments
 (0)