diff --git a/src/group.h b/src/group.h index d81deb4264..fffb0796a8 100644 --- a/src/group.h +++ b/src/group.h @@ -174,6 +174,13 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g /** Rescale a jacobian point by b which must be non-zero. Constant-time. */ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); +/** Convert a group element into a 64-byte array */ +static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *ge); + +/** Convert a 64-byte array into group element. This function assumes that the + * provided buffer correctly encodes a group element. */ +static void secp256k1_ge_from_bytes(secp256k1_ge *ge, const unsigned char *buf); + /** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve. * * In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the diff --git a/src/group_impl.h b/src/group_impl.h index 537be32ff6..1a382afb4f 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_GROUP_IMPL_H #define SECP256K1_GROUP_IMPL_H +#include + #include "field.h" #include "group.h" #include "util.h" @@ -941,4 +943,38 @@ static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp25 return secp256k1_fe_is_square_var(&r); } +static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *ge) { + if (sizeof(secp256k1_ge_storage) == 64) { + secp256k1_ge_storage s; + secp256k1_ge_to_storage(&s, ge); + memcpy(&buf[0], &s, sizeof(s)); + } else { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(buf, &ge->x); + secp256k1_fe_get_b32(buf + 32, &ge->y); + } +} + +static void secp256k1_ge_from_bytes(secp256k1_ge *ge, const unsigned char *buf) { + if (sizeof(secp256k1_ge_storage) == 64) { + /* When the secp256k1_ge_storage type is exactly 64 byte, use its + * representation inside secp256k1_pubkey, as conversion is very fast. + * Note that secp256k1_pubkey_save must use the same representation. */ + secp256k1_ge_storage s; + memcpy(&s, &buf[0], sizeof(s)); + secp256k1_ge_from_storage(ge, &s); + } else { + /* Otherwise, fall back to 32-byte big endian for X and Y. */ + secp256k1_fe x, y; + int ret = 1; + + ret &= secp256k1_fe_set_b32_limit(&x, buf); + ret &= secp256k1_fe_set_b32_limit(&y, buf + 32); + VERIFY_CHECK(ret); + secp256k1_ge_set_xy(ge, &x, &y); + } +} + #endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index 4ed6243fd1..7bd9bf0a13 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -237,39 +237,13 @@ static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, } static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { - if (sizeof(secp256k1_ge_storage) == 64) { - /* When the secp256k1_ge_storage type is exactly 64 byte, use its - * representation inside secp256k1_pubkey, as conversion is very fast. - * Note that secp256k1_pubkey_save must use the same representation. */ - secp256k1_ge_storage s; - memcpy(&s, &pubkey->data[0], sizeof(s)); - secp256k1_ge_from_storage(ge, &s); - } else { - /* Otherwise, fall back to 32-byte big endian for X and Y. */ - secp256k1_fe x, y; - int ret = 1; - - ret &= secp256k1_fe_set_b32_limit(&x, pubkey->data); - ret &= secp256k1_fe_set_b32_limit(&y, pubkey->data + 32); - VERIFY_CHECK(ret); - secp256k1_ge_set_xy(ge, &x, &y); - } + secp256k1_ge_from_bytes(ge, pubkey->data); ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); return 1; } static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { - if (sizeof(secp256k1_ge_storage) == 64) { - secp256k1_ge_storage s; - secp256k1_ge_to_storage(&s, ge); - memcpy(&pubkey->data[0], &s, sizeof(s)); - } else { - VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_normalize_var(&ge->y); - secp256k1_fe_get_b32(pubkey->data, &ge->x); - secp256k1_fe_get_b32(pubkey->data + 32, &ge->y); - } + secp256k1_ge_to_bytes(pubkey->data, ge); } int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { diff --git a/src/tests.c b/src/tests.c index 32946439ff..67378f176b 100644 --- a/src/tests.c +++ b/src/tests.c @@ -4016,6 +4016,25 @@ static void test_add_neg_y_diff_x(void) { CHECK(secp256k1_gej_eq_ge_var(&sumj, &res)); } +static void test_ge_bytes(void) { + unsigned char buf[64]; + unsigned char expected_buf[64] = { + 0x98, 0x17, 0xF8, 0x16, 0x5B, 0x81, 0xF2, 0x59, + 0xD9, 0x28, 0xCE, 0x2D, 0xDB, 0xFC, 0x9B, 0x02, + 0x07, 0x0B, 0x87, 0xCE, 0x95, 0x62, 0xA0, 0x55, + 0xAC, 0xBB, 0xDC, 0xF9, 0x7E, 0x66, 0xBE, 0x79, + 0xB8, 0xD4, 0x10, 0xFB, 0x8F, 0xD0, 0x47, 0x9C, + 0x19, 0x54, 0x85, 0xA6, 0x48, 0xB4, 0x17, 0xFD, + 0xA8, 0x08, 0x11, 0x0E, 0xFC, 0xFB, 0xA4, 0x5D, + 0x65, 0xC4, 0xA3, 0x26, 0x77, 0xDA, 0x3A, 0x48, + }; + secp256k1_ge p, g = secp256k1_ge_const_g; + secp256k1_ge_to_bytes(buf, &g); + CHECK(memcmp(buf, expected_buf, sizeof(buf)) == 0); + secp256k1_ge_from_bytes(&p, buf); + CHECK(secp256k1_ge_eq_var(&p, &g)); +} + static void run_ge(void) { int i; for (i = 0; i < COUNT * 32; i++) { @@ -4023,6 +4042,7 @@ static void run_ge(void) { } test_add_neg_y_diff_x(); test_intialized_inf(); + test_ge_bytes(); } static void test_gej_cmov(const secp256k1_gej *a, const secp256k1_gej *b) {