From 7978205916784fea4db9f8f540be7bd87043425f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1igo=20Querejeta=20Azurmendi?= <31273774+iquerejeta@users.noreply.github.com> Date: Sat, 25 May 2024 18:55:14 +0200 Subject: [PATCH] RFC9381 ECVRF implementation (#1188) * Version 12 of ECVRF * Incorrect ordering of function inputs * Identation and notation * single multiscalar multiplication function Also changed the style of tests, where the expected output is in vrf.exp rather than in test data (following the style of the hashing). * declarations inside the if code block * identation of test_data * Rename to RFC9381 * Move declarations to top of block * Check small order over deserialised PK * Include from_string functions in ed25519_ref10 * Update quirks.h --------- Co-authored-by: Frank Denis <124872+jedisct1@users.noreply.github.com> --- .gitignore | 1 + src/libsodium/Makefile.am | 6 + .../crypto_core/ed25519/core_ed25519.c | 38 +--- .../crypto_core/ed25519/ref10/ed25519_ref10.c | 213 ++++++++++++++---- .../crypto_sign/ed25519/ref10/open.c | 2 +- src/libsodium/crypto_vrf/crypto_vrf.c | 72 ++++++ src/libsodium/crypto_vrf/rfc9381/keypair.c | 40 ++++ src/libsodium/crypto_vrf/rfc9381/prove.c | 69 ++++++ src/libsodium/crypto_vrf/rfc9381/verify.c | 116 ++++++++++ src/libsodium/crypto_vrf/rfc9381/vrf.c | 31 +++ .../crypto_vrf/rfc9381/vrf_rfc9381.h | 10 + src/libsodium/include/Makefile.am | 2 + src/libsodium/include/sodium.h | 2 + src/libsodium/include/sodium/crypto_vrf.h | 75 ++++++ .../include/sodium/crypto_vrf_rfc9381.h | 78 +++++++ .../include/sodium/private/ed25519_ref10.h | 15 +- src/libsodium/include/sodium/private/quirks.h | 3 + test/default/Makefile.am | 8 +- test/default/vrf.c | 91 ++++++++ test/default/vrf.exp | 15 ++ test/symbols/all-symbols.txt | 24 ++ 21 files changed, 822 insertions(+), 89 deletions(-) create mode 100644 src/libsodium/crypto_vrf/crypto_vrf.c create mode 100644 src/libsodium/crypto_vrf/rfc9381/keypair.c create mode 100644 src/libsodium/crypto_vrf/rfc9381/prove.c create mode 100644 src/libsodium/crypto_vrf/rfc9381/verify.c create mode 100644 src/libsodium/crypto_vrf/rfc9381/vrf.c create mode 100644 src/libsodium/crypto_vrf/rfc9381/vrf_rfc9381.h create mode 100644 src/libsodium/include/sodium/crypto_vrf.h create mode 100644 src/libsodium/include/sodium/crypto_vrf_rfc9381.h create mode 100644 test/default/vrf.c create mode 100644 test/default/vrf.exp diff --git a/.gitignore b/.gitignore index be47693d..09628bd7 100644 --- a/.gitignore +++ b/.gitignore @@ -166,6 +166,7 @@ test/default/stream2 test/default/stream3 test/default/stream4 test/default/verify1 +test/default/vrf test/default/xchacha20 test/js.done testing diff --git a/src/libsodium/Makefile.am b/src/libsodium/Makefile.am index c1f26e41..d3ed6d23 100644 --- a/src/libsodium/Makefile.am +++ b/src/libsodium/Makefile.am @@ -99,6 +99,12 @@ libsodium_la_SOURCES = \ crypto_stream/xsalsa20/stream_xsalsa20.c \ crypto_verify/verify.c \ include/sodium/private/asm_cet.h \ + crypto_vrf/crypto_vrf.c \ + crypto_vrf/rfc9381/keypair.c \ + crypto_vrf/rfc9381/prove.c \ + crypto_vrf/rfc9381/verify.c \ + crypto_vrf/rfc9381/vrf.c \ + crypto_vrf/rfc9381/vrf_rfc9381.h \ include/sodium/private/chacha20_ietf_ext.h \ include/sodium/private/common.h \ include/sodium/private/ed25519_ref10.h \ diff --git a/src/libsodium/crypto_core/ed25519/core_ed25519.c b/src/libsodium/crypto_core/ed25519/core_ed25519.c index 0a7a8959..5a075f4a 100644 --- a/src/libsodium/crypto_core/ed25519/core_ed25519.c +++ b/src/libsodium/crypto_core/ed25519/core_ed25519.c @@ -65,41 +65,12 @@ crypto_core_ed25519_from_uniform(unsigned char *p, const unsigned char *r) return 0; } -#define HASH_GE_L 48U - -static int -_string_to_points(unsigned char * const px, const size_t n, - const char *ctx, const unsigned char *msg, size_t msg_len, - int hash_alg) -{ - unsigned char h[crypto_core_ed25519_HASHBYTES]; - unsigned char h_be[2U * HASH_GE_L]; - size_t i, j; - - if (n > 2U) { - abort(); /* LCOV_EXCL_LINE */ - } - if (core_h2c_string_to_hash(h_be, n * HASH_GE_L, ctx, msg, msg_len, - hash_alg) != 0) { - return -1; - } - COMPILER_ASSERT(sizeof h >= HASH_GE_L); - for (i = 0U; i < n; i++) { - for (j = 0U; j < HASH_GE_L; j++) { - h[j] = h_be[i * HASH_GE_L + HASH_GE_L - 1U - j]; - } - memset(&h[j], 0, (sizeof h) - j); - ge25519_from_hash(&px[i * crypto_core_ed25519_BYTES], h); - } - return 0; -} - int crypto_core_ed25519_from_string(unsigned char p[crypto_core_ed25519_BYTES], const char *ctx, const unsigned char *msg, size_t msg_len, int hash_alg) { - return _string_to_points(p, 1, ctx, msg, msg_len, hash_alg); + return ge25519_from_string(p, ctx, msg, msg_len, hash_alg); } int @@ -107,12 +78,7 @@ crypto_core_ed25519_from_string_ro(unsigned char p[crypto_core_ed25519_BYTES], const char *ctx, const unsigned char *msg, size_t msg_len, int hash_alg) { - unsigned char px[2 * crypto_core_ed25519_BYTES]; - - if (_string_to_points(px, 2, ctx, msg, msg_len, hash_alg) != 0) { - return -1; - } - return crypto_core_ed25519_add(p, &px[0], &px[crypto_core_ed25519_BYTES]); + return ge25519_from_string_ro(p, ctx, msg, msg_len, hash_alg); } void diff --git a/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c b/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c index 6acb4b00..c5a318df 100644 --- a/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c +++ b/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c @@ -4,6 +4,7 @@ #include #include "crypto_verify_32.h" +#include "../core_h2c.h" #include "private/common.h" #include "private/ed25519_ref10.h" #include "utils.h" @@ -742,68 +743,76 @@ ge25519_tobytes(unsigned char *s, const ge25519_p2 *h) } /* + * Precomputation of a base point, to use in multiscalar multiplication algorithm A,3A,5A,7A,9A,11A,13A,15A + */ +static void point_precomputation(ge25519_cached cached[8], const ge25519_p3 *base) { + ge25519_p1p1 t; + ge25519_p3 u; + ge25519_p3 A; + + // Precomputation of values of A + ge25519_p3_to_cached(&cached[0], base); + + ge25519_p3_dbl(&t, base); + ge25519_p1p1_to_p3(&A, &t); + + ge25519_add_cached(&t, &A, &cached[0]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&cached[1], &u); + + ge25519_add_cached(&t, &A, &cached[1]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&cached[2], &u); + + ge25519_add_cached(&t, &A, &cached[2]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&cached[3], &u); + + ge25519_add_cached(&t, &A, &cached[3]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&cached[4], &u); + + ge25519_add_cached(&t, &A, &cached[4]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&cached[5], &u); + + ge25519_add_cached(&t, &A, &cached[5]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&cached[6], &u); + + ge25519_add_cached(&t, &A, &cached[6]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&cached[7], &u); +} + +/* + Variable time double scalar multiplication with variable bases r = a * A + b * B where a = a[0]+256*a[1]+...+256^31 a[31]. and b = b[0]+256*b[1]+...+256^31 b[31]. - B is the Ed25519 base point (x,4/5) with x positive. - Only used for signatures verification. + If a null pointer is passed as an argument for B, the function uses + the precomputed values of the base point for the scalar multiplication. + + Only used for ed25519 and VRF verification. */ void ge25519_double_scalarmult_vartime(ge25519_p2 *r, const unsigned char *a, - const ge25519_p3 *A, const unsigned char *b) + const ge25519_p3 *A, const unsigned char *b, + const ge25519_p3 *B) { - static const ge25519_precomp Bi[8] = { -#ifdef HAVE_TI_MODE -# include "fe_51/base2.h" -#else -# include "fe_25_5/base2.h" -#endif - }; signed char aslide[256]; signed char bslide[256]; - ge25519_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge25519_cached Ai[8]; ge25519_p1p1 t; ge25519_p3 u; - ge25519_p3 A2; int i; slide_vartime(aslide, a); slide_vartime(bslide, b); - ge25519_p3_to_cached(&Ai[0], A); - - ge25519_p3_dbl(&t, A); - ge25519_p1p1_to_p3(&A2, &t); - - ge25519_add_cached(&t, &A2, &Ai[0]); - ge25519_p1p1_to_p3(&u, &t); - ge25519_p3_to_cached(&Ai[1], &u); - - ge25519_add_cached(&t, &A2, &Ai[1]); - ge25519_p1p1_to_p3(&u, &t); - ge25519_p3_to_cached(&Ai[2], &u); - - ge25519_add_cached(&t, &A2, &Ai[2]); - ge25519_p1p1_to_p3(&u, &t); - ge25519_p3_to_cached(&Ai[3], &u); - - ge25519_add_cached(&t, &A2, &Ai[3]); - ge25519_p1p1_to_p3(&u, &t); - ge25519_p3_to_cached(&Ai[4], &u); - - ge25519_add_cached(&t, &A2, &Ai[4]); - ge25519_p1p1_to_p3(&u, &t); - ge25519_p3_to_cached(&Ai[5], &u); - - ge25519_add_cached(&t, &A2, &Ai[5]); - ge25519_p1p1_to_p3(&u, &t); - ge25519_p3_to_cached(&Ai[6], &u); - - ge25519_add_cached(&t, &A2, &Ai[6]); - ge25519_p1p1_to_p3(&u, &t); - ge25519_p3_to_cached(&Ai[7], &u); + point_precomputation(Ai, A); ge25519_p2_0(r); @@ -824,12 +833,31 @@ ge25519_double_scalarmult_vartime(ge25519_p2 *r, const unsigned char *a, ge25519_sub_cached(&t, &u, &Ai[(-aslide[i]) / 2]); } - if (bslide[i] > 0) { - ge25519_p1p1_to_p3(&u, &t); - ge25519_add_precomp(&t, &u, &Bi[bslide[i] / 2]); - } else if (bslide[i] < 0) { - ge25519_p1p1_to_p3(&u, &t); - ge25519_sub_precomp(&t, &u, &Bi[(-bslide[i]) / 2]); + if (B == NULL) { + static const ge25519_precomp Bi[8] = { +#ifdef HAVE_TI_MODE +# include "fe_51/base2.h" +#else +# include "fe_25_5/base2.h" +#endif + }; + if (bslide[i] > 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_add_precomp(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_sub_precomp(&t, &u, &Bi[(-bslide[i]) / 2]); + } + } else { + ge25519_cached Bi[8]; + point_precomputation(Bi, B); + if (bslide[i] > 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_add_cached(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_sub_cached(&t, &u, &Bi[(-bslide[i]) / 2]); + } } ge25519_p1p1_to_p2(r, &t); @@ -2224,6 +2252,29 @@ sc25519_invert(unsigned char recip[32], const unsigned char s[32]) sc25519_sqmul(recip, 8, _11101011); } +/* 2^252+27742317777372353535851937790883648493 */ +static const unsigned char L[] = { + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, + 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 +}; + +void +sc25519_negate(unsigned char neg[32], const unsigned char s[32]) +{ + unsigned char t_[64]; + unsigned char s_[64]; + + memset(t_, 0, sizeof t_); + memset(s_, 0, sizeof s_); + memcpy(t_ + 32, L, + 32); + memcpy(s_, s, 32); + sodium_sub(t_, s_, sizeof t_); + sc25519_reduce(t_); + memcpy(neg, t_, 32); +} + /* Input: s[0]+256*s[1]+...+256^63*s[63] = s @@ -2700,6 +2751,68 @@ ge25519_from_uniform(unsigned char s[32], const unsigned char r[32]) ge25519_p3_tobytes(s, &p3); } +#define HASH_GE_L 48U + +static int +_string_to_points(unsigned char * const px, const size_t n, + const char *ctx, const unsigned char *msg, size_t msg_len, + int hash_alg) +{ + unsigned char h[64]; + unsigned char h_be[2U * HASH_GE_L]; + size_t i, j; + + if (n > 2U) { + abort(); /* LCOV_EXCL_LINE */; + } + if (core_h2c_string_to_hash(h_be, n * HASH_GE_L, ctx, msg, msg_len, + hash_alg) != 0) { + return -1; + } + COMPILER_ASSERT(sizeof h >= HASH_GE_L); + for (i = 0U; i < n; i++) { + for (j = 0U; j < HASH_GE_L; j++) { + h[j] = h_be[i * HASH_GE_L + HASH_GE_L - 1U - j]; + } + memset(&h[j], 0, (sizeof h) - j); + ge25519_from_hash(&px[i * 32], h); + } + return 0; +} + +int +ge25519_from_string(unsigned char p[32], + const char *ctx, const unsigned char *msg, + size_t msg_len, int hash_alg) +{ + return _string_to_points(p, 1, ctx, msg, msg_len, hash_alg); + + +} + +int +ge25519_from_string_ro(unsigned char p[32], + const char *ctx, const unsigned char *msg, + size_t msg_len, int hash_alg) +{ + unsigned char px[64]; + ge25519_p3 p_p3, q_p3, r_p3; + + if (_string_to_points(px, 2, ctx, msg, msg_len, hash_alg) != 0) { + return -1; + } + + if (ge25519_frombytes(&p_p3, &px[0]) != 0 || ge25519_is_on_curve(&p_p3) == 0 || + ge25519_frombytes(&q_p3, &px[32]) != 0 || ge25519_is_on_curve(&q_p3) == 0) { + return -1; + } + ge25519_p3_add(&r_p3, &p_p3, &q_p3); + ge25519_p3_tobytes(p, &r_p3); + + return 0; +} + + static void fe25519_reduce64(fe25519 fe_f, const unsigned char h[64]) { diff --git a/src/libsodium/crypto_sign/ed25519/ref10/open.c b/src/libsodium/crypto_sign/ed25519/ref10/open.c index fb999c29..1543faa3 100644 --- a/src/libsodium/crypto_sign/ed25519/ref10/open.c +++ b/src/libsodium/crypto_sign/ed25519/ref10/open.c @@ -55,7 +55,7 @@ _crypto_sign_ed25519_verify_detached(const unsigned char *sig, crypto_hash_sha512_final(&hs, h); sc25519_reduce(h); - ge25519_double_scalarmult_vartime(&sb_ah_p2, h, &A, sig + 32); + ge25519_double_scalarmult_vartime(&sb_ah_p2, h, &A, sig + 32, NULL); ge25519_p2_to_p3(&sb_ah, &sb_ah_p2); ge25519_p3_sub(&check, &expected_r, &sb_ah); diff --git a/src/libsodium/crypto_vrf/crypto_vrf.c b/src/libsodium/crypto_vrf/crypto_vrf.c new file mode 100644 index 00000000..52f024dd --- /dev/null +++ b/src/libsodium/crypto_vrf/crypto_vrf.c @@ -0,0 +1,72 @@ + +#include "crypto_vrf.h" + +size_t +crypto_vrf_publickeybytes(void) +{ + return crypto_vrf_PUBLICKEYBYTES; +} + +size_t +crypto_vrf_secretkeybytes(void) +{ + return crypto_vrf_SECRETKEYBYTES; +} + +size_t +crypto_vrf_seedbytes(void) +{ + return crypto_vrf_SEEDBYTES; +} + +size_t +crypto_vrf_proofbytes(void) +{ + return crypto_vrf_PROOFBYTES; +} + +size_t +crypto_vrf_outputbytes(void) +{ + return crypto_vrf_OUTPUTBYTES; +} + +const char * +crypto_vrf_primitive(void) +{ + return crypto_vrf_PRIMITIVE; +} + +int +crypto_vrf_keypair(unsigned char *pk, unsigned char *sk) +{ + return crypto_vrf_rfc9381_keypair(pk, sk); +} + +int +crypto_vrf_seed_keypair(unsigned char *pk, unsigned char *sk, + const unsigned char *seed) +{ + return crypto_vrf_rfc9381_seed_keypair(pk, sk, seed); +} + +int +crypto_vrf_prove(unsigned char *proof, const unsigned char *m, const unsigned long long mlen, + const unsigned char *skpk) +{ + return crypto_vrf_rfc9381_prove(proof, m, mlen, skpk); +} + +int +crypto_vrf_verify(unsigned char *output, const unsigned char *pk, + const unsigned char *proof, const unsigned char *m, + const unsigned long long mlen) +{ + return crypto_vrf_rfc9381_verify(output, pk, proof, m, mlen); +} + +int +crypto_vrf_proof_to_hash(unsigned char *hash, const unsigned char *proof) +{ + return crypto_vrf_rfc9381_proof_to_hash(hash, proof); +} diff --git a/src/libsodium/crypto_vrf/rfc9381/keypair.c b/src/libsodium/crypto_vrf/rfc9381/keypair.c new file mode 100644 index 00000000..ecefa5e0 --- /dev/null +++ b/src/libsodium/crypto_vrf/rfc9381/keypair.c @@ -0,0 +1,40 @@ +#include + +#include "crypto_hash_sha512.h" +#include "crypto_vrf_rfc9381.h" +#include "private/ed25519_ref10.h" +#include "randombytes.h" +#include "utils.h" + +int +crypto_vrf_rfc9381_seed_keypair(unsigned char *pk, unsigned char *sk, + const unsigned char *seed) +{ + ge25519_p3 A; + + crypto_hash_sha512(sk, seed, 32); + sk[0] &= 248; + sk[31] &= 127; + sk[31] |= 64; + + ge25519_scalarmult_base(&A, sk); + ge25519_p3_tobytes(pk, &A); + + memmove(sk, seed, 32); + memmove(sk + 32, pk, 32); + + return 0; +} + +int +crypto_vrf_rfc9381_keypair(unsigned char *pk, unsigned char *sk) +{ + unsigned char seed[32]; + int ret; + + randombytes_buf(seed, sizeof seed); + ret = crypto_vrf_rfc9381_seed_keypair(pk, sk, seed); + sodium_memzero(seed, sizeof seed); + + return ret; +} diff --git a/src/libsodium/crypto_vrf/rfc9381/prove.c b/src/libsodium/crypto_vrf/rfc9381/prove.c new file mode 100644 index 00000000..1dffed27 --- /dev/null +++ b/src/libsodium/crypto_vrf/rfc9381/prove.c @@ -0,0 +1,69 @@ +#include +#include + +#include "crypto_hash_sha512.h" +#include "crypto_vrf_rfc9381.h" +#include "private/ed25519_ref10.h" +#include "utils.h" +#include "vrf_rfc9381.h" + + +int +crypto_vrf_rfc9381_prove(unsigned char *proof, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk) +{ + + crypto_hash_sha512_state hs; + unsigned char az[64]; + unsigned char H_string[32]; + unsigned char kB_string[32], kH_string[32]; + unsigned char string_to_hash[32 + mlen]; + unsigned char challenge[64], nonce[64]; + ge25519_p3 H, Gamma, kB, kH; + + crypto_hash_sha512(az, sk, 32); + az[0] &= 248; + az[31] &= 127; + az[31] |= 64; + + memmove(string_to_hash, sk + 32, 32); + memmove(string_to_hash + 32, m, mlen); + ge25519_from_string(H_string, "ECVRF_edwards25519_XMD:SHA-512_ELL2_NU_\4", string_to_hash, 32 + mlen, 2); /* elligator2 */ + + ge25519_frombytes(&H, H_string); + ge25519_scalarmult(&Gamma, az, &H); + + crypto_hash_sha512_init(&hs); + crypto_hash_sha512_update(&hs, az + 32, 32); + crypto_hash_sha512_update(&hs, H_string, 32); + crypto_hash_sha512_final(&hs, nonce); + + sc25519_reduce(nonce); + ge25519_scalarmult_base(&kB, nonce); + ge25519_scalarmult(&kH, nonce, &H); + + ge25519_p3_tobytes(proof, &Gamma); + ge25519_p3_tobytes(kB_string, &kB); + ge25519_p3_tobytes(kH_string, &kH); + + crypto_hash_sha512_init(&hs); + crypto_hash_sha512_update(&hs, &SUITE, 1); + crypto_hash_sha512_update(&hs, &TWO, 1); + crypto_hash_sha512_update(&hs, sk + 32, 32); + crypto_hash_sha512_update(&hs, H_string, 32); + crypto_hash_sha512_update(&hs, proof, 32); + crypto_hash_sha512_update(&hs, kB_string, 32); + crypto_hash_sha512_update(&hs, kH_string, 32); + crypto_hash_sha512_update(&hs, &ZERO, 1); + crypto_hash_sha512_final(&hs, challenge); + + memmove(proof + 32, challenge, 16); + memset(challenge + 16, 0, 48); /* we zero out the last 48 bytes of the challenge */ + sc25519_muladd(proof + 48, challenge, az, nonce); + + sodium_memzero(az, sizeof az); + sodium_memzero(nonce, sizeof nonce); + + return 0; +} diff --git a/src/libsodium/crypto_vrf/rfc9381/verify.c b/src/libsodium/crypto_vrf/rfc9381/verify.c new file mode 100644 index 00000000..40bcbb50 --- /dev/null +++ b/src/libsodium/crypto_vrf/rfc9381/verify.c @@ -0,0 +1,116 @@ +#include +#include +#include + +#include "crypto_hash_sha512.h" +#include "crypto_vrf_rfc9381.h" +#include "private/ed25519_ref10.h" +#include "vrf_rfc9381.h" +#include "crypto_verify_16.h" + +int +crypto_vrf_rfc9381_proof_to_hash(unsigned char *beta, + const unsigned char *pi) +{ + ge25519_p3 Gamma; + unsigned char gamma_string[32]; + + if (ge25519_is_canonical(pi) == 0 || + ge25519_frombytes(&Gamma, pi) != 0) { + return -1; + } + + if (pi[48 + 31] & 240 && + sc25519_is_canonical(pi + 48) == 0) { + return -1; + } + + ge25519_clear_cofactor(&Gamma); + ge25519_p3_tobytes(gamma_string, &Gamma); + + /* beta_string = Hash(suite_string || three_string || point_to_string(cofactor * Gamma) || zero_string ) */ + crypto_hash_sha512_state hs; + crypto_hash_sha512_init(&hs); + crypto_hash_sha512_update(&hs, &SUITE, 1); + crypto_hash_sha512_update(&hs, &THREE, 1); + crypto_hash_sha512_update(&hs, gamma_string, 32); + crypto_hash_sha512_update(&hs, &ZERO, 1); + crypto_hash_sha512_final(&hs, beta); + + return 0; +} + +static int +vrf_verify(const unsigned char *pi, + const unsigned char *alpha, unsigned long long alphalen, + const ge25519_p3 *Y_point) +{ + unsigned char H_string[32], U_string[32], V_string[32], Y_string[32]; + unsigned char cn[32], c[32], s[32]; + unsigned char string_to_hash[32 + alphalen], challenge[64]; + + crypto_hash_sha512_state hs; + ge25519_p2 U, V; + ge25519_p3 H, Gamma; + ge25519_p1p1 tmp_p1p1_point; + ge25519_cached tmp_cached_point; + + ge25519_p3_tobytes(Y_string, Y_point); + + if (ge25519_is_canonical(pi) == 0 || + ge25519_frombytes(&Gamma, pi) != 0) { + return -1; + } + + memmove(c, pi + 32, 16); /* c = pi[32:48] */ + memmove(s, pi + 48, 32); /* s = pi[48:80] */ + + if (s[31] & 240 && + sc25519_is_canonical(s) == 0) { + return -1; + } + + memset(c + 16, 0, 16); + + memmove(string_to_hash, Y_string, 32); + memmove(string_to_hash + 32, alpha, alphalen); + ge25519_from_string(H_string, "ECVRF_edwards25519_XMD:SHA-512_ELL2_NU_\4", string_to_hash, 32 + alphalen, 2); /* elligator2 */ + + ge25519_frombytes(&H, H_string); + sc25519_negate(cn, c); /* negate scalar c */ + + ge25519_double_scalarmult_vartime(&U, cn, Y_point, s, NULL); + + ge25519_double_scalarmult_vartime(&V, cn, &Gamma, s, &H); + + ge25519_tobytes(U_string, &U); + ge25519_tobytes(V_string, &V); + + crypto_hash_sha512_init(&hs); + crypto_hash_sha512_update(&hs, &SUITE, 1); + crypto_hash_sha512_update(&hs, &TWO, 1); + crypto_hash_sha512_update(&hs, Y_string, 32); + crypto_hash_sha512_update(&hs, H_string, 32); + crypto_hash_sha512_update(&hs, pi, 32); + crypto_hash_sha512_update(&hs, U_string, 32); + crypto_hash_sha512_update(&hs, V_string, 32); + crypto_hash_sha512_update(&hs, &ZERO, 1); + crypto_hash_sha512_final(&hs, challenge); + + return crypto_verify_16(c, challenge); +} + +int +crypto_vrf_rfc9381_verify(unsigned char *output, + const unsigned char *pk, + const unsigned char *proof, + const unsigned char *msg, const unsigned long long msglen) +{ + ge25519_p3 Y; + if (ge25519_frombytes(&Y, pk) == 0 && ge25519_has_small_order(&Y) == 0 && + ge25519_is_canonical(pk) == 1 && (vrf_verify(proof, msg, msglen, &Y) == 0)) { + return crypto_vrf_rfc9381_proof_to_hash(output, proof); + } else { + return -1; + } +} diff --git a/src/libsodium/crypto_vrf/rfc9381/vrf.c b/src/libsodium/crypto_vrf/rfc9381/vrf.c new file mode 100644 index 00000000..f6660b9d --- /dev/null +++ b/src/libsodium/crypto_vrf/rfc9381/vrf.c @@ -0,0 +1,31 @@ +#include "crypto_vrf_rfc9381.h" + +size_t +crypto_vrf_rfc9381_bytes(void) +{ + return crypto_vrf_rfc9381_BYTES; +} + +size_t +crypto_vrf_rfc9381_outputbytes(void) +{ + return crypto_vrf_rfc9381_OUTPUTBYTES; +} + +size_t +crypto_vrf_rfc9381_seedbytes(void) +{ + return crypto_vrf_rfc9381_SEEDBYTES; +} + +size_t +crypto_vrf_rfc9381_publickeybytes(void) +{ + return crypto_vrf_rfc9381_PUBLICKEYBYTES; +} + +size_t +crypto_vrf_rfc9381_secretkeybytes(void) +{ + return crypto_vrf_rfc9381_SECRETKEYBYTES; +} diff --git a/src/libsodium/crypto_vrf/rfc9381/vrf_rfc9381.h b/src/libsodium/crypto_vrf/rfc9381/vrf_rfc9381.h new file mode 100644 index 00000000..2d263632 --- /dev/null +++ b/src/libsodium/crypto_vrf/rfc9381/vrf_rfc9381.h @@ -0,0 +1,10 @@ +#ifndef vrf_rfc9381_H +#define vrf_rfc9381_H + +static const unsigned char SUITE = 0x04; /* ECVRF-ED25519-SHA512-ELL2 */ + +static const unsigned char ZERO = 0x00; +static const unsigned char TWO = 0x02; +static const unsigned char THREE = 0x03; + +#endif diff --git a/src/libsodium/include/Makefile.am b/src/libsodium/include/Makefile.am index d639c634..5d5cdbf3 100644 --- a/src/libsodium/include/Makefile.am +++ b/src/libsodium/include/Makefile.am @@ -59,6 +59,8 @@ SODIUM_EXPORT = \ sodium/crypto_verify_16.h \ sodium/crypto_verify_32.h \ sodium/crypto_verify_64.h \ + sodium/crypto_vrf.h \ + sodium/crypto_vrf_rfc9381.h \ sodium/export.h \ sodium/randombytes.h \ sodium/randombytes_internal_random.h \ diff --git a/src/libsodium/include/sodium.h b/src/libsodium/include/sodium.h index ff788e34..5a50bedb 100644 --- a/src/libsodium/include/sodium.h +++ b/src/libsodium/include/sodium.h @@ -51,6 +51,8 @@ #include "sodium/crypto_verify_16.h" #include "sodium/crypto_verify_32.h" #include "sodium/crypto_verify_64.h" +#include "sodium/crypto_vrf.h" +#include "sodium/crypto_vrf_rfc9381.h" #include "sodium/randombytes.h" #include "sodium/randombytes_internal_random.h" #include "sodium/randombytes_sysrandom.h" diff --git a/src/libsodium/include/sodium/crypto_vrf.h b/src/libsodium/include/sodium/crypto_vrf.h new file mode 100644 index 00000000..e3b65b94 --- /dev/null +++ b/src/libsodium/include/sodium/crypto_vrf.h @@ -0,0 +1,75 @@ +#ifndef crypto_vrf_H +#define crypto_vrf_H + +/* + * THREAD SAFETY: crypto_vrf_keypair() is thread-safe, + * provided that sodium_init() was called before. + * + * Other functions, including crypto_vrf_keypair_from_seed(), are always thread-safe. + */ + +#include + +#include "crypto_vrf_rfc9381.h" +#include "export.h" + +#ifdef __cplusplus +# ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wlong-long" +# endif +extern "C" { +#endif + +#define crypto_vrf_PROOFBYTES crypto_vrf_rfc9381_BYTES +SODIUM_EXPORT +size_t crypto_vrf_bytes(void); + +#define crypto_vrf_OUTPUTBYTES crypto_vrf_rfc9381_OUTPUTBYTES +SODIUM_EXPORT +size_t crypto_vrf_outputbytes(void); + +#define crypto_vrf_SEEDBYTES crypto_vrf_rfc9381_SEEDBYTES +SODIUM_EXPORT +size_t crypto_vrf_seedbytes(void); + +#define crypto_vrf_PUBLICKEYBYTES crypto_vrf_rfc9381_PUBLICKEYBYTES +SODIUM_EXPORT +size_t crypto_vrf_publickeybytes(void); + +#define crypto_vrf_SECRETKEYBYTES crypto_vrf_rfc9381_SECRETKEYBYTES +SODIUM_EXPORT +size_t crypto_vrf_secretkeybytes(void); + +#define crypto_vrf_PRIMITIVE "rfc9381" +SODIUM_EXPORT +const char *crypto_vrf_primitive(void); + +SODIUM_EXPORT +int crypto_vrf_keypair(unsigned char *pk, unsigned char *sk) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +int crypto_vrf_seed_keypair(unsigned char *pk, unsigned char *sk, + const unsigned char *seed) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +int crypto_vrf_prove(unsigned char *proof, const unsigned char *m, + unsigned long long mlen, const unsigned char *sk) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +int crypto_vrf_verify(unsigned char *output, + const unsigned char *pk, + const unsigned char *proof, + const unsigned char *m, unsigned long long mlen) + __attribute__ ((warn_unused_result)) __attribute__ ((nonnull)); + +SODIUM_EXPORT +int crypto_vrf_proof_to_hash(unsigned char *hash, const unsigned char *proof); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libsodium/include/sodium/crypto_vrf_rfc9381.h b/src/libsodium/include/sodium/crypto_vrf_rfc9381.h new file mode 100644 index 00000000..14bf2d06 --- /dev/null +++ b/src/libsodium/include/sodium/crypto_vrf_rfc9381.h @@ -0,0 +1,78 @@ + +#ifndef crypto_vrf_rfc9381_H +#define crypto_vrf_rfc9381_H + +#include + +#include "export.h" + +#ifdef __cplusplus +# ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wlong-long" +# endif +extern "C" { +#endif + +#define crypto_vrf_rfc9381_BYTES 80U +SODIUM_EXPORT +size_t crypto_vrf_rfc9381_bytes(void); + +#define crypto_vrf_rfc9381_OUTPUTBYTES 64U +SODIUM_EXPORT +size_t crypto_vrf_rfc9381_outputbytes(void); + +#define crypto_vrf_rfc9381_SEEDBYTES 32U +SODIUM_EXPORT +size_t crypto_vrf_rfc9381_seedbytes(void); + +#define crypto_vrf_rfc9381_PUBLICKEYBYTES 32U +SODIUM_EXPORT +size_t crypto_vrf_rfc9381_publickeybytes(void); + +#define crypto_vrf_rfc9381_SECRETKEYBYTES 64U +SODIUM_EXPORT +size_t crypto_vrf_rfc9381_secretkeybytes(void); + +SODIUM_EXPORT +int crypto_vrf_rfc9381_prove(unsigned char *proof, + const unsigned char *m, + unsigned long long mlen, const unsigned char *sk); + +SODIUM_EXPORT +int crypto_vrf_rfc9381_verify(unsigned char *output, + const unsigned char *pk, + const unsigned char *proof, + const unsigned char *m, + unsigned long long mlen) + __attribute__ ((warn_unused_result)) __attribute__ ((nonnull)); + +SODIUM_EXPORT +int crypto_vrf_rfc9381_proof_to_hash(unsigned char *hash, + const unsigned char *proof) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +int crypto_vrf_rfc9381_keypair(unsigned char *pk, unsigned char *sk) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +int crypto_vrf_rfc9381_seed_keypair(unsigned char *pk, + unsigned char *sk, + const unsigned char *seed) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +void crypto_vrf_rfc9381_sk_to_seed(unsigned char *seed, + const unsigned char *sk) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +void crypto_vrf_rfc9381_sk_to_pk(unsigned char *pk, + const unsigned char *sk) + __attribute__ ((nonnull)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libsodium/include/sodium/private/ed25519_ref10.h b/src/libsodium/include/sodium/private/ed25519_ref10.h index 9c66cd9d..6c5f73da 100644 --- a/src/libsodium/include/sodium/private/ed25519_ref10.h +++ b/src/libsodium/include/sodium/private/ed25519_ref10.h @@ -96,7 +96,8 @@ void ge25519_scalarmult_base(ge25519_p3 *h, const unsigned char *a); void ge25519_double_scalarmult_vartime(ge25519_p2 *r, const unsigned char *a, const ge25519_p3 *A, - const unsigned char *b); + const unsigned char *b, + const ge25519_p3 *B); void ge25519_scalarmult(ge25519_p3 *h, const unsigned char *a, const ge25519_p3 *p); @@ -115,6 +116,14 @@ void ge25519_from_uniform(unsigned char s[32], const unsigned char r[32]); void ge25519_from_hash(unsigned char s[32], const unsigned char h[64]); +int ge25519_from_string(unsigned char p[32], + const char *ctx, const unsigned char *msg, + size_t msg_len, int hash_alg); + +int ge25519_from_string_ro(unsigned char p[32], + const char *ctx, const unsigned char *msg, + size_t msg_len, int hash_alg); + /* Ristretto group */ @@ -132,6 +141,8 @@ void ristretto255_from_hash(unsigned char s[32], const unsigned char h[64]); void sc25519_invert(unsigned char recip[32], const unsigned char s[32]); +void sc25519_negate(unsigned char neg[32], const unsigned char s[32]); + void sc25519_reduce(unsigned char s[64]); void sc25519_mul(unsigned char s[32], const unsigned char a[32], @@ -142,4 +153,6 @@ void sc25519_muladd(unsigned char s[32], const unsigned char a[32], int sc25519_is_canonical(const unsigned char s[32]); +void ge25519_clear_cofactor(ge25519_p3 *p3); + #endif diff --git a/src/libsodium/include/sodium/private/quirks.h b/src/libsodium/include/sodium/private/quirks.h index fa474b7c..ffd861ee 100644 --- a/src/libsodium/include/sodium/private/quirks.h +++ b/src/libsodium/include/sodium/private/quirks.h @@ -54,6 +54,8 @@ #define ge25519_clear_cofactor _sodium_ge25519_clear_cofactor #define ge25519_double_scalarmult_vartime _sodium_ge25519_double_scalarmult_vartime #define ge25519_from_hash _sodium_ge25519_from_hash +#define ge25519_from_string _sodium_ge25519_from_string +#define ge25519_from_string_ro _sodium_ge25519_from_string_ro #define ge25519_from_uniform _sodium_ge25519_from_uniform #define ge25519_frombytes _sodium_ge25519_frombytes #define ge25519_frombytes_negate_vartime _sodium_ge25519_frombytes_negate_vartime @@ -77,6 +79,7 @@ #define sc25519_is_canonical _sodium_sc25519_is_canonical #define sc25519_mul _sodium_sc25519_mul #define sc25519_muladd _sodium_sc25519_muladd +#define sc25519_negate _sodium_sc25519_negate #define sc25519_reduce _sodium_sc25519_reduce #define softaes_block_encrypt _sodium_softaes_block_encrypt diff --git a/test/default/Makefile.am b/test/default/Makefile.am index 4ecace11..9b33c4d7 100644 --- a/test/default/Makefile.am +++ b/test/default/Makefile.am @@ -86,6 +86,7 @@ EXTRA_DIST = \ stream3.exp \ stream4.exp \ verify1.exp \ + vrf.exp \ xchacha20.exp DISTCLEANFILES = \ @@ -171,6 +172,7 @@ DISTCLEANFILES = \ stream3.res \ stream4.res \ verify1.res \ + vrf.res \ xchacha20.res AM_CPPFLAGS = \ @@ -253,7 +255,8 @@ TESTS_TARGETS = \ stream2 \ stream3 \ stream4 \ - verify1 + verify1 \ + vrf if !EMSCRIPTEN TESTS_TARGETS += \ @@ -511,6 +514,9 @@ stream4_LDADD = $(TESTS_LDADD) verify1_SOURCE = cmptest.h verify1.c verify1_LDADD = $(TESTS_LDADD) +vrf_SOURCE = cmptest.h vrf.c +vrf_LDADD = $(TESTS_LDADD) + xchacha20_SOURCE = cmptest.h xchacha20.c xchacha20_LDADD = $(TESTS_LDADD) diff --git a/test/default/vrf.c b/test/default/vrf.c new file mode 100644 index 00000000..c4929864 --- /dev/null +++ b/test/default/vrf.c @@ -0,0 +1,91 @@ + +#define TEST_NAME "vrf" +#include "cmptest.h" + +typedef struct TestData_ { + const char seed[2 * 32 + 1]; +} TestData; +/* + * Test data taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-09#appendix-A.4 + * which contains the seeds. The expected values for the pk, proof and output are in vrf.exp + */ +static const TestData test_data[] = { + {"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"}, + {"4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb"}, + {"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7"}, +}; + +static const unsigned char messages[3][2] = {{0x00}, {0x72}, {0xaf, 0x82}}; + +int main(void) +{ + unsigned char *seed; + unsigned char sk[64]; + unsigned char pk[32]; + unsigned char proof[80]; + unsigned char output[64]; + unsigned int i; + + char pk_hex[32 * 2 + 1]; + char proof_hex[80 * 2 + 1]; + char output_hex[64 * 2 + 1]; + + seed = (unsigned char *) sodium_malloc(crypto_vrf_rfc9381_SEEDBYTES); + + assert(crypto_vrf_rfc9381_SECRETKEYBYTES == 64); + assert(crypto_vrf_rfc9381_PUBLICKEYBYTES == 32); + assert(crypto_vrf_rfc9381_SEEDBYTES == 32); + assert(crypto_vrf_rfc9381_BYTES == 80); + assert(crypto_vrf_rfc9381_OUTPUTBYTES == 64); + + for (i = 0U; i < (sizeof test_data) / (sizeof test_data[0]); i++) { + sodium_hex2bin(seed, 32, + test_data[i].seed, (size_t) -1U, NULL, NULL, NULL); + + crypto_vrf_rfc9381_seed_keypair(pk, sk, seed); + printf("%s\n", sodium_bin2hex(pk_hex, sizeof pk_hex, pk, sizeof pk)); + + if (crypto_vrf_rfc9381_prove(proof, messages[i], i, sk) != 0){ + printf("crypto_vrf_prove() error: [%u]\n", i); + } + printf("%s\n", sodium_bin2hex(proof_hex, sizeof proof_hex, proof, sizeof proof)); + + if (crypto_vrf_rfc9381_verify(output, pk, proof, messages[i], i) != 0){ + printf("verify error: [%u]\n", i); + } + printf("%s\n", sodium_bin2hex(output_hex, sizeof output_hex, output, sizeof output)); + + proof[0] ^= 0x01; + if (crypto_vrf_rfc9381_verify(output, pk, proof, messages[i], i) == 0){ + printf("verify succeeded with bad gamma: [%u]\n", i); + } + proof[0] ^= 0x01; + proof[32] ^= 0x01; + if (crypto_vrf_rfc9381_verify(output, pk, proof, messages[i], i) == 0){ + printf("verify succeeded with bad c value: [%u]\n", i); + } + proof[32] ^= 0x01; + proof[48] ^= 0x01; + if (crypto_vrf_rfc9381_verify(output, pk, proof, messages[i], i) == 0){ + printf("verify succeeded with bad s value: [%u]\n", i); + } + proof[48] ^= 0x01; + proof[79] ^= 0x80; + if (crypto_vrf_rfc9381_verify(output, pk, proof, messages[i], i) == 0){ + printf("verify succeeded with bad s value (high-order-bit flipped): [%u]\n", i); + } + proof[79] ^= 0x80; + + if (i > 0) { + if (crypto_vrf_rfc9381_verify(output, pk, proof, messages[i], i-1) == 0){ + printf("verify succeeded with truncated message: [%u]\n", i); + } + } + + if (crypto_vrf_rfc9381_proof_to_hash(output, proof) != 0){ + printf("crypto_vrf_proof_to_hash() error: [%u]\n", i); + } + printf("%s\n\n", sodium_bin2hex(output_hex, sizeof output_hex, output, sizeof output)); + } + return 0; +} diff --git a/test/default/vrf.exp b/test/default/vrf.exp new file mode 100644 index 00000000..7395a87d --- /dev/null +++ b/test/default/vrf.exp @@ -0,0 +1,15 @@ +d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a +7d9c633ffeee27349264cf5c667579fc583b4bda63ab71d001f89c10003ab46f14adf9a3cd8b8412d9038531e865c341cafa73589b023d14311c331a9ad15ff2fb37831e00f0acaa6d73bc9997b06501 +9d574bf9b8302ec0fc1e21c3ec5368269527b87b462ce36dab2d14ccf80c53cccf6758f058c5b1c856b116388152bbe509ee3b9ecfe63d93c3b4346c1fbc6c54 +9d574bf9b8302ec0fc1e21c3ec5368269527b87b462ce36dab2d14ccf80c53cccf6758f058c5b1c856b116388152bbe509ee3b9ecfe63d93c3b4346c1fbc6c54 + +3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c +47b327393ff2dd81336f8a2ef10339112401253b3c714eeda879f12c509072ef055b48372bb82efbdce8e10c8cb9a2f9d60e93908f93df1623ad78a86a028d6bc064dbfc75a6a57379ef855dc6733801 +38561d6b77b71d30eb97a062168ae12b667ce5c28caccdf76bc88e093e4635987cd96814ce55b4689b3dd2947f80e59aac7b7675f8083865b46c89b2ce9cc735 +38561d6b77b71d30eb97a062168ae12b667ce5c28caccdf76bc88e093e4635987cd96814ce55b4689b3dd2947f80e59aac7b7675f8083865b46c89b2ce9cc735 + +fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025 +926e895d308f5e328e7aa159c06eddbe56d06846abf5d98c2512235eaa57fdce35b46edfc655bc828d44ad09d1150f31374e7ef73027e14760d42e77341fe05467bb286cc2c9d7fde29120a0b2320d04 +121b7f9b9aaaa29099fc04a94ba52784d44eac976dd1a3cca458733be5cd090a7b5fbd148444f17f8daf1fb55cb04b1ae85a626e30a54b4b0f8abf4a43314a58 +121b7f9b9aaaa29099fc04a94ba52784d44eac976dd1a3cca458733be5cd090a7b5fbd148444f17f8daf1fb55cb04b1ae85a626e30a54b4b0f8abf4a43314a58 + diff --git a/test/symbols/all-symbols.txt b/test/symbols/all-symbols.txt index 08b215da..a48d745d 100644 --- a/test/symbols/all-symbols.txt +++ b/test/symbols/all-symbols.txt @@ -637,6 +637,27 @@ crypto_verify_32 crypto_verify_32_bytes crypto_verify_64 crypto_verify_64_bytes +crypto_vrf_keypair +crypto_vrf_outputbytes +crypto_vrf_primitive +crypto_vrf_proof_to_hash +crypto_vrf_proofbytes +crypto_vrf_prove +crypto_vrf_publickeybytes +crypto_vrf_rfc9381_bytes +crypto_vrf_rfc9381_keypair +crypto_vrf_rfc9381_outputbytes +crypto_vrf_rfc9381_proof_to_hash +crypto_vrf_rfc9381_prove +crypto_vrf_rfc9381_publickeybytes +crypto_vrf_rfc9381_secretkeybytes +crypto_vrf_rfc9381_seed_keypair +crypto_vrf_rfc9381_seedbytes +crypto_vrf_rfc9381_verify +crypto_vrf_secretkeybytes +crypto_vrf_seed_keypair +crypto_vrf_seedbytes +crypto_vrf_verify escrypt_PBKDF2_SHA256 escrypt_alloc_region escrypt_free_local @@ -653,6 +674,8 @@ fe25519_tobytes ge25519_clear_cofactor ge25519_double_scalarmult_vartime ge25519_from_hash +ge25519_from_string +ge25519_from_string_ro ge25519_from_uniform ge25519_frombytes ge25519_frombytes_negate_vartime @@ -686,6 +709,7 @@ sc25519_invert sc25519_is_canonical sc25519_mul sc25519_muladd +sc25519_negate sc25519_reduce sodium_add sodium_allocarray