Skip to content

Commit 2e4ed39

Browse files
committed
f add tweak functions for xonly_pubkeys
1 parent add8e78 commit 2e4ed39

File tree

4 files changed

+374
-10
lines changed

4 files changed

+374
-10
lines changed

include/secp256k1.h

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,120 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_create(
776776
const unsigned char *seckey
777777
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
778778

779+
/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey. This
780+
* function optionally outputs a sign bit that can be used to convert
781+
* the secp256k1_xonly_pubkey back into the same secp256k1_pubkey.
782+
* The sign bit is 0 if the input pubkey encodes a positive point (has a Y
783+
* coordinate that is a quadratic residue), otherwise it is 1.
784+
*
785+
* Returns: 1 if the public key was successfully converted
786+
* 0 otherwise
787+
*
788+
* Args: ctx: pointer to a context object
789+
* Out: xonly_pubkey: pointer to an x-only public key object for placing the
790+
* converted public key (cannot be NULL)
791+
* sign: sign bit of the pubkey. Can be used to reconstruct a
792+
* public key from x-only public key (can be NULL)
793+
* In: pubkey: pointer to a public key that is converted (cannot be
794+
* NULL)
795+
*/
796+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey(
797+
const secp256k1_context* ctx,
798+
secp256k1_xonly_pubkey *xonly_pubkey,
799+
int *sign,
800+
const secp256k1_pubkey *pubkey
801+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
802+
803+
/** Convert a secp256k1_xonly_pubkey into a secp256k1_pubkey. If this
804+
* function is used to invert secp256k1_xonly_pubkey_from_pubkey, the
805+
* sign bit must be set to the output of that function. If the sign bit
806+
* is 0 the output pubkey encodes a positive point (has a Y coordinate that is a
807+
* quadratic residue), otherwise it is negative.
808+
*
809+
* Returns: 1 if the public key was successfully converted
810+
* 0 otherwise
811+
*
812+
* Args: ctx: pointer to a context object
813+
* Out: pubkey: pointer to a public key object for placing the
814+
* converted public key (cannot be NULL)
815+
* In: xonly_pubkey: pointer to an x-only public key that is converted
816+
* (cannot be NULL)
817+
* sign: sign bit of the resulting public key (can be NULL)
818+
*/
819+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_to_pubkey(
820+
const secp256k1_context* ctx,
821+
secp256k1_pubkey *pubkey,
822+
const secp256k1_xonly_pubkey *xonly_pubkey,
823+
int sign
824+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
825+
826+
/** Tweak the private key of an x-only pubkey by adding a tweak to it. The public
827+
* key of the resulting private key will be the same as the output of
828+
* secp256k1_xonly_pubkey_tweak_add called with the same tweak and corresponding
829+
* input public key.
830+
*
831+
* If the public key corresponds to a positive point, tweak32 is added to the
832+
* seckey (modulo the group order). If the public key corresponds to a
833+
* negative point, tweak32 is added to the negation of the seckey (modulo the
834+
* group order).
835+
*
836+
* Returns: 1 if the tweak was successfully added to seckey
837+
* 0 if the tweak was out of range or the resulting private key would be
838+
* invalid (only when the tweak is the complement of the private key) or
839+
* seckey is 0.
840+
*
841+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
842+
* In/Out: seckey: pointer to a 32-byte private key
843+
* In: tweak32: pointer to a 32-byte tweak
844+
*/
845+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_privkey_tweak_add(
846+
const secp256k1_context* ctx,
847+
unsigned char *seckey,
848+
const unsigned char *tweak32
849+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
850+
851+
/** Tweak an x-only public key by adding tweak times the generator to it. Note that
852+
* the output is a secp256k1_pubkey and not a secp256k1_xonly_pubkey.
853+
* Returns: 1 if tweak times the generator was successfully added to pubkey
854+
* 0 if the tweak was out of range or the resulting public key would be
855+
* invalid (only when the tweak is the complement of the corresponding
856+
* private key).
857+
*
858+
* Args: ctx: pointer to a context object initialized for validation
859+
* (cannot be NULL)
860+
* Out: output_pubkey: pointer to a public key object (cannot be NULL)
861+
* In: internal_pubkey: pointer to an x-only public key object to apply the
862+
* tweak to (cannot be NULL)
863+
* tweak32: pointer to a 32-byte tweak (cannot be NULL)
864+
*/
865+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add(
866+
const secp256k1_context* ctx,
867+
secp256k1_pubkey *output_pubkey,
868+
const secp256k1_xonly_pubkey *internal_pubkey,
869+
const unsigned char *tweak32
870+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
871+
872+
/** Verifies that output_pubkey is the result of calling
873+
* secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32.
874+
*
875+
* Returns: 1 if output_pubkey is the result of tweaking the internal_pubkey with
876+
* tweak32
877+
* 0 otherwise
878+
*
879+
* Args: ctx: pointer to a context object initialized for validation
880+
* (cannot be NULL)
881+
* In: output_pubkey: pointer to a public key object (cannot be NULL)
882+
* internal_pubkey: pointer to an x-only public key object to apply the
883+
* tweak to (cannot be NULL)
884+
* tweak32: pointer to a 32-byte tweak (cannot be NULL)
885+
*/
886+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_verify(
887+
const secp256k1_context* ctx,
888+
const secp256k1_pubkey *output_pubkey,
889+
const secp256k1_xonly_pubkey *internal_pubkey,
890+
const unsigned char *tweak32
891+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
892+
779893
#ifdef __cplusplus
780894
}
781895
#endif

src/modules/schnorrsig/tests_impl.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,43 @@ void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) {
718718
}
719719
#undef N_SIGS
720720

721+
void test_schnorrsig_taproot(void) {
722+
unsigned char sk[32];
723+
secp256k1_xonly_pubkey internal_pk;
724+
unsigned char internal_pk_bytes[32];
725+
secp256k1_pubkey output_pk;
726+
secp256k1_xonly_pubkey output_pk_pos;
727+
unsigned char output_pk_bytes[32];
728+
unsigned char tweak[32];
729+
int sign;
730+
unsigned char msg[32];
731+
secp256k1_schnorrsig sig;
732+
733+
/* Create output key */
734+
secp256k1_rand256(sk);
735+
CHECK(secp256k1_xonly_pubkey_create(ctx, &internal_pk, sk) == 1);
736+
memset(tweak, 1, sizeof(tweak));
737+
CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_pk, tweak) == 1);
738+
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_pk_pos, &sign, &output_pk) == 1);
739+
CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk_bytes, &output_pk_pos) == 1);
740+
741+
/* Key spend */
742+
secp256k1_rand256(msg);
743+
CHECK(secp256k1_xonly_privkey_tweak_add(ctx, sk, tweak) == 1);
744+
CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 1);
745+
/* Verify key spend */
746+
CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk_pos, output_pk_bytes) == 1);
747+
CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &output_pk_pos) == 1);
748+
749+
/* Script spend */
750+
CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1);
751+
/* Verify script spend */
752+
CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk_pos, output_pk_bytes) == 1);
753+
CHECK(secp256k1_xonly_pubkey_parse(ctx, &internal_pk, internal_pk_bytes) == 1);
754+
CHECK(secp256k1_xonly_pubkey_to_pubkey(ctx, &output_pk, &output_pk_pos, sign) == 1);
755+
CHECK(secp256k1_xonly_pubkey_tweak_verify(ctx, &output_pk, &internal_pk, tweak) == 1);
756+
}
757+
721758
void run_schnorrsig_tests(void) {
722759
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);
723760

@@ -728,6 +765,7 @@ void run_schnorrsig_tests(void) {
728765
/* test_schnorrsig_bip_vectors(scratch); */
729766
test_schnorrsig_sign();
730767
test_schnorrsig_sign_verify(scratch);
768+
test_schnorrsig_taproot();
731769

732770
secp256k1_scratch_space_destroy(ctx, scratch);
733771
}

src/secp256k1.c

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -730,13 +730,20 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
730730
return 1;
731731
}
732732

733-
/* Converts a point into its absolute value, i.e. keeps it as is if it is
734-
* positive and otherwise negates it. */
735-
static void secp256k1_ec_pubkey_absolute(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) {
736-
secp256k1_ge ge;
733+
/* Converts the point encoded by a secp256k1_pubkey into its absolute value,
734+
* i.e. keeps it as is if it is positive and otherwise negates it. Sign is set
735+
* to 1 if the input point was negative and set to 0 otherwise. */
736+
static void secp256k1_ec_pubkey_absolute(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, int *sign) {
737+
secp256k1_ge ge;
737738
secp256k1_pubkey_load(ctx, &ge, pubkey);
739+
if (sign != NULL) {
740+
*sign = 0;
741+
}
738742
if (!secp256k1_fe_is_quad_var(&ge.y)) {
739743
secp256k1_ge_neg(&ge, &ge);
744+
if (sign != NULL) {
745+
*sign = 1;
746+
}
740747
}
741748
secp256k1_pubkey_save(pubkey, &ge);
742749
}
@@ -750,7 +757,7 @@ int secp256k1_xonly_pubkey_create(const secp256k1_context* ctx, secp256k1_xonly_
750757
if (!secp256k1_ec_pubkey_create(ctx, (secp256k1_pubkey *) pubkey, seckey)) {
751758
return 0;
752759
}
753-
secp256k1_ec_pubkey_absolute(ctx, (secp256k1_pubkey *) pubkey);
760+
secp256k1_ec_pubkey_absolute(ctx, (secp256k1_pubkey *) pubkey, NULL);
754761
return 1;
755762
}
756763

@@ -766,7 +773,7 @@ int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_p
766773
if (!secp256k1_ec_pubkey_parse(ctx, (secp256k1_pubkey *) pubkey, buf, sizeof(buf))) {
767774
return 0;
768775
}
769-
secp256k1_ec_pubkey_absolute(ctx, (secp256k1_pubkey *) pubkey);
776+
secp256k1_ec_pubkey_absolute(ctx, (secp256k1_pubkey *) pubkey, NULL);
770777
return 1;
771778
}
772779

@@ -785,6 +792,74 @@ int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char
785792
return 1;
786793
}
787794

795+
int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *sign, const secp256k1_pubkey *pubkey) {
796+
VERIFY_CHECK(ctx != NULL);
797+
ARG_CHECK(xonly_pubkey != NULL);
798+
ARG_CHECK(pubkey != NULL);
799+
800+
*xonly_pubkey = *(secp256k1_xonly_pubkey *) pubkey;
801+
802+
secp256k1_ec_pubkey_absolute(ctx, (secp256k1_pubkey *) xonly_pubkey, sign);
803+
return 1;
804+
}
805+
806+
int secp256k1_xonly_pubkey_to_pubkey(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_xonly_pubkey *xonly_pubkey, int sign) {
807+
VERIFY_CHECK(ctx != NULL);
808+
ARG_CHECK(pubkey != NULL);
809+
ARG_CHECK(xonly_pubkey != NULL);
810+
811+
*pubkey = *(secp256k1_pubkey *) xonly_pubkey;
812+
if (sign) {
813+
return secp256k1_ec_pubkey_negate(ctx, pubkey);
814+
}
815+
return 1;
816+
}
817+
818+
int secp256k1_xonly_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey32, const unsigned char *tweak32) {
819+
secp256k1_ge ge;
820+
secp256k1_pubkey pubkey;
821+
VERIFY_CHECK(ctx != NULL);
822+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
823+
ARG_CHECK(seckey32 != NULL);
824+
ARG_CHECK(tweak32 != NULL);
825+
826+
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, seckey32)) {
827+
return 0;
828+
}
829+
secp256k1_pubkey_load(ctx, &ge, &pubkey);
830+
if (!secp256k1_fe_is_quad_var(&ge.y)) {
831+
if (!secp256k1_ec_privkey_negate(ctx, seckey32)) {
832+
return 0;
833+
}
834+
}
835+
836+
return secp256k1_ec_privkey_tweak_add(ctx, seckey32, tweak32);
837+
}
838+
839+
int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
840+
VERIFY_CHECK(ctx != NULL);
841+
ARG_CHECK(internal_pubkey != NULL);
842+
ARG_CHECK(output_pubkey != NULL);
843+
ARG_CHECK(tweak32 != NULL);
844+
845+
*output_pubkey = *(secp256k1_pubkey *)internal_pubkey;
846+
return secp256k1_ec_pubkey_tweak_add(ctx, output_pubkey, tweak32);
847+
}
848+
849+
int secp256k1_xonly_pubkey_tweak_verify(const secp256k1_context* ctx, const secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
850+
secp256k1_pubkey pk_expected;
851+
VERIFY_CHECK(ctx != NULL);
852+
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
853+
ARG_CHECK(internal_pubkey != NULL);
854+
ARG_CHECK(output_pubkey != NULL);
855+
ARG_CHECK(tweak32 != NULL);
856+
857+
if (!secp256k1_xonly_pubkey_tweak_add(ctx, &pk_expected, internal_pubkey, tweak32)) {
858+
return 0;
859+
}
860+
return memcmp(&pk_expected, output_pubkey, sizeof(pk_expected)) == 0;
861+
}
862+
788863
#ifdef ENABLE_MODULE_ECDH
789864
# include "modules/ecdh/main_impl.h"
790865
#endif

0 commit comments

Comments
 (0)