Skip to content

Commit

Permalink
Add new API for mapping random byte strings to curve points.
Browse files Browse the repository at this point in the history
  • Loading branch information
dfaranha committed Dec 23, 2024
1 parent 94d8cfa commit 8858084
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 113 deletions.
6 changes: 6 additions & 0 deletions bench/bench_ep.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,12 @@ static void arith(void) {
BENCH_ADD(ep_map(p, msg, 5));
} BENCH_END;

BENCH_RUN("ep_map_rnd") {
uint8_t msg[5];
rand_bytes(msg, 5);
BENCH_ADD(ep_map_rnd(p, msg, 5));

This comment has been minimized.

Copy link
@bellebaum

bellebaum Jan 8, 2025

This might be reading a lot more off the stack than five bytes

This comment has been minimized.

Copy link
@dfaranha

dfaranha Jan 25, 2025

Author Contributor

Fixed!

} BENCH_END;

#if EP_MAP == BASIC || !defined(STRIP)
BENCH_RUN("ep_map_basic") {
uint8_t msg[5];
Expand Down
9 changes: 9 additions & 0 deletions include/relic_ep.h
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,15 @@ void ep_map_sswum(ep_t p, const uint8_t *msg, size_t len);
*/
void ep_map_swift(ep_t p, const uint8_t *msg, size_t len);

/**
* Maps a random byte array to a point in a prime elliptic curve.
*
* @param[out] p - the result.
* @param[in] uniform_bytes - the random byte array to map.
* @param[in] len - the array length in bytes.
*/
void ep_map_rnd(ep_t p, const uint8_t *uniform_bytes, size_t len);

/**
* Compresses a point.
*
Expand Down
268 changes: 155 additions & 113 deletions src/ep/relic_ep_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
/* Private definitions */
/*============================================================================*/

/**
* Domain separation string.
*/
#define MAP_STRING (const uint8_t *)"RELIC"

This comment has been minimized.

Copy link
@bellebaum

bellebaum Jan 8, 2025

Since this serves as a domain separation tag, it would be nice if one could overwrite it using a compiler option -DMAP_STRING=my_dst, which I think would require ifndef here?

This comment has been minimized.

Copy link
@dfaranha

dfaranha Jan 10, 2025

Author Contributor

That's easy, but I understood you wanted an API to be able to do this outside the library? As in, hash in whatever manner you want (with SHAKE and a different DST) and ask the library for the mapping?

This comment has been minimized.

Copy link
@bellebaum

bellebaum Jan 10, 2025

Correct, my thought was that for some applications this would be easier, if they just want domain separation from other applications. I do not need this directly :)

This comment has been minimized.

Copy link
@dfaranha

dfaranha Jan 11, 2025

Author Contributor

Fixed in HEAD!


#ifdef EP_CTMAP

/**
Expand Down Expand Up @@ -69,7 +74,43 @@ TMPL_MAP_SSWU(ep, fp, dig_t);
*/
TMPL_MAP_SVDW(ep, fp, dig_t);

#undef EP_MAP_copy_sec
static void ep_map_basic_impl(ep_t p, const uint8_t *bytes, size_t len) {
bn_t x;
fp_t t0;

bn_null(x);
fp_null(t0);

RLC_TRY {
bn_new(x);
fp_new(t0);

bn_read_bin(x, bytes, len);
fp_prime_conv(p->x, x);
fp_set_dig(p->z, 1);

while (1) {
ep_rhs(t0, p->x);

if (fp_smb(t0) == 1) {
fp_srt(p->y, t0);
p->coord = BASIC;
break;
}

fp_add_dig(p->x, p->x, 1);
}

ep_mul_cof(p, p);
}
RLC_CATCH_ANY {
RLC_THROW(ERR_CAUGHT);
}
RLC_FINALLY {
bn_free(x);
fp_free(t0);
}
}

/**
* Maps an array of uniformly random bytes to a point in a prime elliptic
Expand All @@ -82,31 +123,27 @@ TMPL_MAP_SVDW(ep, fp, dig_t);
* @param[in] len - the array length in bytes.
* @param[in] map_fn - the mapping function.
*/
static void ep_map_from_field(ep_t p, const uint8_t *uniform_bytes, size_t len,
static void ep_map_sswum_impl(ep_t p, const uint8_t *bytes, size_t len,

This comment has been minimized.

Copy link
@bellebaum

bellebaum Jan 8, 2025

Should this function check len == 2*elm?

This comment has been minimized.

Copy link
@dfaranha

dfaranha Jan 25, 2025

Author Contributor

I handled errors in the outer function, that is ep_map_rnd().

void (*const map_fn)(ep_t, const fp_t)) {
bn_t k;
fp_t t;
ep_t q;
int neg;
/* enough space for two field elements plus extra bytes for uniformity */
const size_t len_per_elm = (FP_PRIME + ep_param_level() + 7) / 8;
const size_t elm = (FP_PRIME + ep_param_level() + 7) / 8;

bn_null(k);
fp_null(t);
ep_null(q);

RLC_TRY {
if (len != 2 * len_per_elm) {
RLC_THROW(ERR_NO_VALID);
}

bn_new(k);
fp_new(t);
ep_new(q);

#define EP_MAP_CONVERT_BYTES(IDX) \
do { \
bn_read_bin(k, uniform_bytes + IDX * len_per_elm, len_per_elm); \
bn_read_bin(k, bytes + IDX * elm, elm); \
fp_prime_conv(t, k); \
} while (0)

Expand Down Expand Up @@ -153,107 +190,11 @@ static void ep_map_from_field(ep_t p, const uint8_t *uniform_bytes, size_t len,
}
}

/*============================================================================*/
/* Public definitions */
/*============================================================================*/

#if EP_MAP == BASIC || !defined(STRIP)

void ep_map_basic(ep_t p, const uint8_t *msg, size_t len) {
bn_t x;
fp_t t0;
uint8_t h[RLC_MD_LEN];

bn_null(x);
fp_null(t0);

RLC_TRY {
bn_new(x);
fp_new(t0);

md_map(h, msg, len);
bn_read_bin(x, h, RLC_MIN(RLC_FP_BYTES, RLC_MD_LEN));

fp_zero(p->x);
fp_prime_conv(p->x, x);
fp_set_dig(p->z, 1);

while (1) {
ep_rhs(t0, p->x);

if (fp_smb(t0) == 1) {
fp_srt(p->y, t0);
p->coord = BASIC;
break;
}

fp_add_dig(p->x, p->x, 1);
}

ep_mul_cof(p, p);
}
RLC_CATCH_ANY {
RLC_THROW(ERR_CAUGHT);
}
RLC_FINALLY {
bn_free(x);
fp_free(t0);
}
}

#endif

#if EP_MAP == SSWUM || !defined(STRIP)

void ep_map_sswum(ep_t p, const uint8_t *msg, size_t len) {
/* enough space for two field elements plus extra bytes for uniformity */
const size_t elm = (FP_PRIME + ep_param_level() + 7) / 8;
uint8_t *r = RLC_ALLOCA(uint8_t, 2 * elm);

RLC_TRY {
/* for hash_to_field, need to hash to a pseudorandom string */
/* XXX(rsw) the below assumes that we want to use MD_MAP for hashing.
* Consider making the hash function a per-curve option!
*/
md_xmd(r, 2 * elm, msg, len, (const uint8_t *)"RELIC", 5);
/* figure out which hash function to use */
const int abNeq0 = (ep_curve_opt_a() != RLC_ZERO) &&
(ep_curve_opt_b() != RLC_ZERO);
void (*const map_fn)(ep_t, const fp_t) =
(ep_curve_is_ctmap() || abNeq0 ? ep_map_sswu : ep_map_svdw);
ep_map_from_field(p, r, 2 * elm, map_fn);
}
RLC_CATCH_ANY {
RLC_THROW(ERR_CAUGHT);
}
RLC_FINALLY {
RLC_FREE(r);
}
}

#endif

#if EP_MAP == SWIFT || !defined(STRIP)

void ep_map_swift(ep_t p, const uint8_t *msg, size_t len) {
/* enough space for two field elements plus extra bytes for uniformity */
const size_t len_per_elm = (FP_PRIME + ep_param_level() + 7) / 8;
uint8_t s, *pseudo_random_bytes = RLC_ALLOCA(uint8_t, 2 * len_per_elm + 1);
static void ep_map_swift_impl(ep_t p, const uint8_t *random, size_t len) {
fp_t h[8], t1, t2, v, w, y, x1, x2, x3, d[3];
ctx_t *ctx = core_get();
bn_t k;

if (ep_curve_is_super()) {
RLC_FREE(pseudo_random_bytes);
RLC_THROW(ERR_NO_CONFIG);
return;
}

if (ctx->mod18 % 3 == 2) {
RLC_FREE(pseudo_random_bytes);
RLC_THROW(ERR_NO_CONFIG);
return;
}
uint8_t s;

bn_null(k);
fp_null(v);
Expand Down Expand Up @@ -286,14 +227,11 @@ void ep_map_swift(ep_t p, const uint8_t *msg, size_t len) {
fp_new(h[i]);
}

md_xmd(pseudo_random_bytes, 2 * len_per_elm + 1, msg, len,
(const uint8_t *)"RELIC", 5);

bn_read_bin(k, pseudo_random_bytes, len_per_elm);
bn_read_bin(k, random, len / 2);
fp_prime_conv(t1, k);
bn_read_bin(k, pseudo_random_bytes + len_per_elm, len_per_elm);
bn_read_bin(k, random + len / 2, len / 2);
fp_prime_conv(t2, k);
s = pseudo_random_bytes[2 * len_per_elm] & 1;
s = random[len - 1] & 1;

if (ep_curve_opt_b() == RLC_ZERO) {
/* h0 = t1^2, h1 = h0^2, h2 = 4a, h3 = h2^3, h4 = h0 * h1 + h3. */
Expand Down Expand Up @@ -489,11 +427,115 @@ void ep_map_swift(ep_t p, const uint8_t *msg, size_t len) {
fp_free(d[0]);
fp_free(d[1]);
fp_free(d[2]);
RLC_FREE(pseudo_random_bytes);
for (size_t i = 0; i < 8; i++) {
fp_free(h[i]);
}
}
}

/*============================================================================*/
/* Public definitions */
/*============================================================================*/

#if EP_MAP == BASIC || !defined(STRIP)

void ep_map_basic(ep_t p, const uint8_t *msg, size_t len) {
/* enough space for two field elements plus extra bytes for uniformity */
const size_t elm = (FP_PRIME + ep_param_level() + 7) / 8;
uint8_t *r = RLC_ALLOCA(uint8_t, elm);

RLC_TRY {
md_xmd(r, elm, msg, len, MAP_STRING, sizeof(MAP_STRING));
ep_map_basic_impl(p, r, elm);
}
RLC_CATCH_ANY {
RLC_THROW(ERR_CAUGHT);
}
RLC_FINALLY {
RLC_FREE(r);
}
}

#endif

#if EP_MAP == SSWUM || !defined(STRIP)

void ep_map_sswum(ep_t p, const uint8_t *msg, size_t len) {
/* enough space for two field elements plus extra bytes for uniformity */
const size_t elm = (FP_PRIME + ep_param_level() + 7) / 8;
uint8_t *r = RLC_ALLOCA(uint8_t, 2 * elm);

RLC_TRY {
/* for hash_to_field, need to hash to a pseudorandom string */
/* XXX(rsw) the below assumes that we want to use MD_MAP for hashing.
* Consider making the hash function a per-curve option!
*/
md_xmd(r, 2 * elm, msg, len, MAP_STRING, sizeof(MAP_STRING));
/* figure out which hash function to use */
const int abNeq0 = (ep_curve_opt_a() != RLC_ZERO) &&
(ep_curve_opt_b() != RLC_ZERO);
void (*const map_fn)(ep_t, const fp_t) =
(ep_curve_is_ctmap() || abNeq0 ? ep_map_sswu : ep_map_svdw);

ep_map_sswum_impl(p, r, len, map_fn);
}
RLC_CATCH_ANY {
RLC_THROW(ERR_CAUGHT);
}
RLC_FINALLY {
RLC_FREE(r);
}
}

#endif

#if EP_MAP == SWIFT || !defined(STRIP)

void ep_map_swift(ep_t p, const uint8_t *msg, size_t len) {
/* enough space for two field elements plus extra bytes for uniformity */
const size_t elm = (FP_PRIME + ep_param_level() + 7) / 8;
uint8_t *r = RLC_ALLOCA(uint8_t, 2 * elm + 1);
ctx_t *ctx = core_get();

if (ep_curve_is_super()) {
RLC_FREE(r);
RLC_THROW(ERR_NO_CONFIG);
return;
}

if (ctx->mod18 % 3 == 2) {
RLC_FREE(r);
RLC_THROW(ERR_NO_CONFIG);
return;
}

RLC_TRY {
md_xmd(r, 2 * elm + 1, msg, len, MAP_STRING, sizeof(MAP_STRING));

ep_map_swift_impl(p, r, 2 * elm + 1);
}
RLC_CATCH_ANY {
RLC_THROW(ERR_CAUGHT);
}
RLC_FINALLY {
RLC_FREE(r);
}
}

#endif

void ep_map_rnd(ep_t p, const uint8_t *uniform_bytes, size_t len) {
#if EP_MAP == BASIC || !defined(STRIP)
ep_map_basic_impl(p, uniform_bytes, len);
#elif EP_MAP == SWIFT || !defined(STRIP)
/* figure out which hash function to use */
const int abNeq0 = (ep_curve_opt_a() != RLC_ZERO) &&
(ep_curve_opt_b() != RLC_ZERO);
void (*const map_fn)(ep_t, const fp_t) =
(ep_curve_is_ctmap() || abNeq0 ? ep_map_sswu : ep_map_svdw);

ep_map_sswum_impl(p, bytes, len, map_fn);

This comment has been minimized.

Copy link
@bellebaum

bellebaum Jan 8, 2025

bytes should say uniform_bytes

This comment has been minimized.

Copy link
@dfaranha

dfaranha Jan 10, 2025

Author Contributor

Fixed!

#elif EP_MAP == SSWUM || !defined(STRIP)
ep_map_swift_impl(p, uniform_bytes, len);
#endif
}
4 changes: 4 additions & 0 deletions test/test_ep.c
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,10 @@ static int hashing(void) {
TEST_ASSERT(ep_on_curve(a) && ep_is_infty(a) == 0, end);
ep_mul(a, a, n);
TEST_ASSERT(ep_on_curve(a) && ep_is_infty(a) == 1, end);
ep_map_rnd(a, msg, sizeof(msg));
TEST_ASSERT(ep_on_curve(a) && ep_is_infty(a) == 0, end);
ep_mul(a, a, n);
TEST_ASSERT(ep_on_curve(a) && ep_is_infty(a) == 1, end);
}
TEST_END;

Expand Down

0 comments on commit 8858084

Please sign in to comment.