Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow sign-to-contract commitments in schnorrsigs [̶a̶l̶t̶e̶r̶n̶a̶t̶i̶v̶e̶]̶ #589

Closed

Conversation

jonasnick
Copy link
Contributor

@jonasnick jonasnick commented Feb 14, 2019

This is an alternative to #588 as requested by @real-or-random which I also slightly prefer (EDIT: this seems to be generally preferred now). The main difference is that the sign-to-contract commitment step happens in the signature function and not the nonce function. Also the nonce_is_negated argument in schnorrsig_sign is replaced by an s2c_opening object. A new argument to schnorrsig_sign is added called s2c_data. There's no need to add a context argument to nonce functions. I also added parsing and serialization for s2c_openings. Manual initialization of s2c_opening is not necessary anymore.

Example:

/* Signer */
secp256k1_s2c_opening opening;
unsigned char s2c_data[32];
secp256k1_schnorrsig_sign(sign, &sig, &opening, msg, sk1, &s2c_data, NULL, NULL);

/* Verifier */
secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, s2c_data, &opening);

@gmaxwell
Copy link
Contributor

That workflow doesn't make it look as easy to implement s2c anti-sidechannel blinding, if I understand it.

@jonasnick
Copy link
Contributor Author

Hm, I've done a quick and dirty rebase of the anti-nonce-sidechannel and it seems straight forward. It's even a bit simpler on the client side.

@jonasnick
Copy link
Contributor Author

I opened PR #590 to show the anti nonce-sidechannel protocol can be built on this PR in a similar way.

@real-or-random
Copy link
Contributor

Concept ACK

@real-or-random
Copy link
Contributor

I guess this is a more general discussion because it's also in ECDSA:
What's the exact benefit of the user being able to provide a nonce function (and possibly shoot herself in the foot?) I'm not sure if there are meaningful cases,so I'm somewhat curious if anybody currently uses this in practice with ECDSA.

@gmaxwell
Copy link
Contributor

@real-or-random IIRC it's actually used by joinmarket to do ECDH with the R value for a stealth payment donation feature. (like one of the outputs pays to P+H(kP)G for some pubkey P). The payments can be found by the recipient simply scanning every signature in the chain, performing ECDH with it, and checking the outputs...

Less cosmically RFC6979 is kind of absurd and actually makes signing measurably slower compared to just a simple hash-- for applications that care about signing speed (like matt's betterhash) substituting it is probably a good idea. Not sure who if anyone is doing that right now, but some things probably should be. (Though those same applications should probably also do batch nonce generation and other stuff that wouldn't really be simple with the current nonce function interface)

Re: footgunnery, I haven't yet seen anyone footgun themselves that way. Creating a working nonce function takes a fair amount of work, so I think it's likely that if anyone bothers they at least have a fighting chance to get it right. I think the best mental model is that we're trying to defend against users that are equal parts lazy and ignorant. Give them a nonce argument and they will set the nonce to private key xor messagehash (actual example which demonstrates lazy+ignorant), but give them a nonce function argument and they'll leave it null unless they actually have a real reason to do otherwise. :)

@real-or-random
Copy link
Contributor

Okay, convinced.

I think the best mental model is that we're trying to defend against users that are equal parts lazy and ignorant.

That's indeed a good way to think about it.

Maybe it's still a good idea to make it more prominent in the docs that people should use the default unless they really know what they are doing.

@gmaxwell
Copy link
Contributor

Sounds fine to me. I also think we could explicitly say that the nonce function isn't an interface that we particularly care about keeping stable.

src/secp256k1.c Outdated Show resolved Hide resolved
src/secp256k1.c Outdated Show resolved Hide resolved
include/secp256k1.h Outdated Show resolved Hide resolved
src/secp256k1.c Outdated Show resolved Hide resolved
@apoelstra
Copy link
Contributor

valgrind ./tests 1 shows a bunch of branches on uninitialized memory.

src/secp256k1.c Outdated
return secp256k1_gej_is_infinity(&pj);
}

static char s2c_opening_magic[8] = { 0x5d, 0x05, 0x20, 0xb8, 0xb7, 0xf2, 0xb1, 0x68 };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it's char but in the struct it's unsigned char.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to uint64_t because there's less room for mistakes and it's easier to read.

secp256k1_ge_set_gej(&r, &rj);
if (s2c_opening != NULL) {
secp256k1_s2c_opening_init(s2c_opening);
if (s2c_data32 != NULL) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a check if s2c_opening == NULL || s2c_data32 == NULL? Because if that's true I think it means the user made a mistake

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean CHECK((s2c_opening != NULL) == (s2c_data32 != NULL)) (both should be NULL or both should be non-NULL)? Yes, I'll add an ARG_CHECK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

/* Public nonce before applying the sign-to-contract commitment */
secp256k1_pubkey original_pubnonce;
/* Integer indicating if signing algorithm negated the nonce */
unsigned char nonce_is_negated;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a computational optimization to avoid computing the jacobi symbol on secp256k1_schnorrsig_verify_s2c_commit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we could could compute the ec_commitment of original_pubnonce and data then negate if this is not a valid nonce. But this prevents batch verification. I will mention this in the doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment to explain this

@elichai
Copy link
Contributor

elichai commented Jul 1, 2019

(I also prefer this design over #588)

@jonasnick jonasnick changed the title Allow sign-to-contract commitments in schnorrsigs [alternative] Allow sign-to-contract commitments in schnorrsigs ~~[alternative]~~ Jul 3, 2019
@jonasnick jonasnick changed the title Allow sign-to-contract commitments in schnorrsigs ~~[alternative]~~ Allow sign-to-contract commitments in schnorrsigs [̶a̶l̶t̶e̶r̶n̶a̶t̶i̶v̶e̶]̶ Jul 3, 2019
/* Byte indicating if signing algorithm negated the nonce. Alternatively when
* verifying we could compute the EC commitment of original_pubnonce and the
* data and negate if this would not be a valid nonce. But this would prevent
* batch verification of sign-to-contract commitments. */
unsigned char nonce_is_negated;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, feel free to ignore: Boolean values are typically represented by int and not unsigned char.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but ints are the worst. Since nonce_is_negated is only ever 0 or 1 I changed this to int.

@jonasnick
Copy link
Contributor Author

rebased on schnorrsigs PR and squashed

SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_s2c_opening_parse(
const secp256k1_context* ctx,
secp256k1_s2c_opening* opening,
const unsigned char *input34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth it to optimize this down to 33 bytes by just using a single bit to mark if the nonce has been negated?(we can use the same byte that we use to signal the parity)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

secp256k1_pubkey_save(&s2c_opening->original_pubnonce, &r);
secp256k1_ec_commit_seckey(ctx, buf, &s2c_opening->original_pubnonce, s2c_data32, 32);
secp256k1_scalar_set_b32(&k, buf, NULL);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
Copy link
Contributor

@elichai elichai Jul 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of recomputing the secret key and multiplying by the generator you can compute H(R, data)*G and add that to the R you already have?
(although since all of this is constant time it might result in no performance change? I'm not sure about it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's R + H(R, data)*G vs. k*G. The latter is less code and faster (no addition). This is independent of being constant time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the performance will be a matter of will this wrap around the order or not. (if it will cause a wrap than k*G is gonna be faster, if it doesn't then calculating the H(R,data)*G and adding to R should be faster i think)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Adding two curve points is completely separate from "wrapping around the order".

src/secp256k1.c Outdated
/* Return commitment == commitment_tmp */
secp256k1_gej_set_infinity(&pj);
secp256k1_pubkey_load(ctx, &p, &commitment_tmp);
secp256k1_gej_add_ge_var(&pj, &pj, &p, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use secp256k1_gej_set_ge() instead of setting to infinity and adding

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor Author

@jonasnick jonasnick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added commit to make the opening serialization 33 bytes instead of 34.

secp256k1_pubkey_save(&s2c_opening->original_pubnonce, &r);
secp256k1_ec_commit_seckey(ctx, buf, &s2c_opening->original_pubnonce, s2c_data32, 32);
secp256k1_scalar_set_b32(&k, buf, NULL);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's R + H(R, data)*G vs. k*G. The latter is less code and faster (no addition). This is independent of being constant time.

SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_s2c_opening_parse(
const secp256k1_context* ctx,
secp256k1_s2c_opening* opening,
const unsigned char *input34
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

@jonasnick
Copy link
Contributor Author

This PR is quite outdated. Sign-to-contract should use the schnorrsig_sign_custom API. PR #1018 is a WIP continuation of this work. Closing.

@jonasnick jonasnick closed this Mar 24, 2022
benma added a commit to benma/secp256k1 that referenced this pull request Sep 11, 2022
Adapted from bitcoin-core#589.

Co-authored-by: Marko Bencun <mbencun+pgp@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Sep 11, 2022
Adapted from bitcoin-core#589.

The data is hashed using a tagged hash with the "s2c/schnorr/data"
tag, which is consistent with the data hashing done in the ECDSA
version of sign-to-contract (in ElementsProject/secp256k1-zkp), where
the "s2c/ecdsa/data" tag is used.

Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Sep 11, 2022
Adapted from bitcoin-core#589.

Co-authored-by: Marko Bencun <mbencun+pgp@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Sep 11, 2022
Adapted from bitcoin-core#589.

The data is hashed using a tagged hash with the "s2c/schnorr/data"
tag, which is consistent with the data hashing done in the ECDSA
version of sign-to-contract (in ElementsProject/secp256k1-zkp), where
the "s2c/ecdsa/data" tag is used. Similarly, the tweak hash tag is
"s2c/schnorr/point".

Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Sep 12, 2022
Adapted from bitcoin-core#589.

Co-authored-by: Marko Bencun <mbencun+pgp@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Sep 12, 2022
Adapted from bitcoin-core#589.

The data is hashed using a tagged hash with the "s2c/schnorr/data"
tag, which is consistent with the data hashing done in the ECDSA
version of sign-to-contract (in ElementsProject/secp256k1-zkp), where
the "s2c/ecdsa/data" tag is used. Similarly, the tweak hash tag is
"s2c/schnorr/point".

Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Sep 13, 2022
Adapted from bitcoin-core#589.

Co-authored-by: Marko Bencun <mbencun+pgp@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Sep 13, 2022
Adapted from bitcoin-core#589.

The data is hashed using a tagged hash with the "s2c/schnorr/data"
tag, which is consistent with the data hashing done in the ECDSA
version of sign-to-contract (in ElementsProject/secp256k1-zkp), where
the "s2c/ecdsa/data" tag is used. Similarly, the tweak hash tag is
"s2c/schnorr/point".

Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request May 1, 2023
Adapted from bitcoin-core#589.

Co-authored-by: Marko Bencun <mbencun+pgp@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request May 1, 2023
Adapted from bitcoin-core#589.

The data is hashed using a tagged hash with the "s2c/schnorr/data"
tag, which is consistent with the data hashing done in the ECDSA
version of sign-to-contract (in ElementsProject/secp256k1-zkp), where
the "s2c/ecdsa/data" tag is used. Similarly, the tweak hash tag is
"s2c/schnorr/point".

Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Jul 29, 2023
Adapted from bitcoin-core#589.

Co-authored-by: Marko Bencun <mbencun+pgp@gmail.com>
benma added a commit to benma/secp256k1 that referenced this pull request Jul 29, 2023
Adapted from bitcoin-core#589.

The data is hashed using a tagged hash with the "s2c/schnorr/data"
tag, which is consistent with the data hashing done in the ECDSA
version of sign-to-contract (in ElementsProject/secp256k1-zkp), where
the "s2c/ecdsa/data" tag is used. Similarly, the tweak hash tag is
"s2c/schnorr/point".

Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants