diff --git a/ChangeLog b/ChangeLog index 2209386a..8fa78d46 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,7 +15,9 @@ not to be detected. - Test vectors from Project Wycheproof have been added. - New low-level APIs for arithmetic mod the order of the prime order group: `crypto_core_ed25519_scalar_random()`, `crypto_core_ed25519_scalar_reduce()`, -and `crypto_core_ed25519_scalar_invert()`. +`crypto_core_ed25519_scalar_invert()`, `crypto_core_ed25519_scalar_negate()`, +`crypto_core_ed25519_scalar_complement()`, `crypto_core_ed25519_scalar_add()` +and `crypto_core_ed25519_scalar_sub()`. - New low-level APIs for scalar multiplication without clamping: `crypto_scalarmult_ed25519_base_noclamp()`, and `crypto_scalarmult_ed25519_noclamp()`. These new APIs are diff --git a/src/libsodium/crypto_core/ed25519/core_ed25519.c b/src/libsodium/crypto_core/ed25519/core_ed25519.c index f17cda13..c412891a 100644 --- a/src/libsodium/crypto_core/ed25519/core_ed25519.c +++ b/src/libsodium/crypto_core/ed25519/core_ed25519.c @@ -1,4 +1,6 @@ +#include + #include "crypto_core_ed25519.h" #include "private/common.h" #include "private/ed25519_ref10.h" @@ -86,6 +88,88 @@ crypto_core_ed25519_scalar_invert(unsigned char *recip, const unsigned char *s) return - sodium_is_zero(s, crypto_core_ed25519_SCALARBYTES); } +void +crypto_core_ed25519_scalar_negate(unsigned char *neg, const unsigned char *s) +{ + /* 2^252+27742317777372353535851937790883648493 */ + static const unsigned char L[32] = { + 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 + }; + uint_fast16_t c = 0U; + unsigned char r = 0U; + size_t i; + + for (i = 0; i < crypto_core_ed25519_SCALARBYTES; i++) { + r |= s[i]; + c = (uint_fast16_t) L[i] - (uint_fast16_t) s[i] - c; + neg[i] = (unsigned char) c; + c = (c >> 8) & 1U; + } + r = ~(r - 1U) >> 8; + for (i = 0; i < crypto_core_ed25519_SCALARBYTES; i++) { + neg[i] &= r; + } +} + +void +crypto_core_ed25519_scalar_complement(unsigned char *comp, const unsigned char *s) +{ + /* 2^252+27742317777372353535851937790883648493 + 1 */ + static const unsigned char L1[32] = { + 0xee, 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 + }; + uint_fast16_t c = 0U; + unsigned char q = 0U; + unsigned char r = 0U; + size_t i; + + q |= s[0] ^ 1U; + r |= s[0]; + c = (uint_fast16_t) L1[0] - (uint_fast16_t) s[0] - c; + comp[0] = (unsigned char) c; + c = (c >> 8) & 1U; + for (i = 1U; i < crypto_core_ed25519_SCALARBYTES; i++) { + q |= s[i]; + r |= s[i]; + c = (uint_fast16_t) L1[i] - (uint_fast16_t) s[i] - c; + comp[i] = (unsigned char) c; + c = (c >> 8) & 1U; + } + q = ~(q - 1U) >> 8; + r = ~(r - 1U) >> 8; + for (i = 0; i < crypto_core_ed25519_SCALARBYTES; i++) { + comp[i] &= q & r; + } + comp[0] |= (~r) & 1U; +} + +void +crypto_core_ed25519_scalar_add(unsigned char *z, const unsigned char *x, + const unsigned char *y) +{ + unsigned char x_[crypto_core_ed25519_NONREDUCEDSCALARBYTES] = { 0U }; + unsigned char y_[crypto_core_ed25519_NONREDUCEDSCALARBYTES] = { 0U }; + + memcpy(x_, x, crypto_core_ed25519_SCALARBYTES); + memcpy(y_, y, crypto_core_ed25519_SCALARBYTES); + sodium_add(x_, y_, crypto_core_ed25519_SCALARBYTES); + crypto_core_ed25519_scalar_reduce(z, x_); +} + +void +crypto_core_ed25519_scalar_sub(unsigned char *z, const unsigned char *x, + const unsigned char *y) +{ + unsigned char yn[crypto_core_ed25519_SCALARBYTES]; + + crypto_core_ed25519_scalar_negate(yn, y); + crypto_core_ed25519_scalar_add(z, x, yn); +} + void crypto_core_ed25519_scalar_reduce(unsigned char *r, const unsigned char *s) diff --git a/src/libsodium/include/sodium/crypto_core_ed25519.h b/src/libsodium/include/sodium/crypto_core_ed25519.h index b3958c7e..eb736ffb 100644 --- a/src/libsodium/include/sodium/crypto_core_ed25519.h +++ b/src/libsodium/include/sodium/crypto_core_ed25519.h @@ -50,6 +50,24 @@ SODIUM_EXPORT int crypto_core_ed25519_scalar_invert(unsigned char *recip, const unsigned char *s) __attribute__ ((nonnull)); +SODIUM_EXPORT +void crypto_core_ed25519_scalar_negate(unsigned char *neg, const unsigned char *s) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +void crypto_core_ed25519_scalar_complement(unsigned char *comp, const unsigned char *s) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +void crypto_core_ed25519_scalar_add(unsigned char *z, const unsigned char *x, + const unsigned char *y) + __attribute__ ((nonnull)); + +SODIUM_EXPORT +void crypto_core_ed25519_scalar_sub(unsigned char *z, const unsigned char *x, + const unsigned char *y) + __attribute__ ((nonnull)); + /* * The interval `s` is sampled from should be at least 317 bits to ensure almost * uniformity of `r` over `L`. diff --git a/test/default/core_ed25519.c b/test/default/core_ed25519.c index 368e9735..e884d2ff 100644 --- a/test/default/core_ed25519.c +++ b/test/default/core_ed25519.c @@ -174,6 +174,43 @@ main(void) printf("crypto_core_ed25519_scalar_reduce() failed\n"); } + randombytes_buf(h, crypto_core_ed25519_UNIFORMBYTES); + crypto_core_ed25519_from_uniform(p, h); + memcpy(p2, p, crypto_core_ed25519_BYTES); + crypto_core_ed25519_scalar_random(sc); + if (crypto_scalarmult_ed25519_noclamp(p, sc, p) != 0) { + printf("crypto_scalarmult_ed25519_noclamp() failed (1)\n"); + } + crypto_core_ed25519_scalar_complement(sc, sc); + if (crypto_scalarmult_ed25519_noclamp(p2, sc, p2) != 0) { + printf("crypto_scalarmult_ed25519_noclamp() failed (2)\n"); + } + crypto_core_ed25519_add(p3, p, p2); + crypto_core_ed25519_from_uniform(p, h); + crypto_core_ed25519_sub(p, p, p3); + assert(p[0] == 0x01); + for (i = 1; i < crypto_core_ed25519_BYTES; i++) { + assert(p[i] == 0); + } + + randombytes_buf(h, crypto_core_ed25519_UNIFORMBYTES); + crypto_core_ed25519_from_uniform(p, h); + memcpy(p2, p, crypto_core_ed25519_BYTES); + crypto_core_ed25519_scalar_random(sc); + if (crypto_scalarmult_ed25519_noclamp(p, sc, p) != 0) { + printf("crypto_scalarmult_ed25519_noclamp() failed (3)\n"); + } + crypto_core_ed25519_scalar_negate(sc, sc); + if (crypto_scalarmult_ed25519_noclamp(p2, sc, p2) != 0) { + printf("crypto_scalarmult_ed25519_noclamp() failed (4)\n"); + } + crypto_core_ed25519_add(p, p, p2); + assert(p[0] == 0x01); + for (i = 1; i < crypto_core_ed25519_BYTES; i++) { + assert(p[i] == 0); + } + + sodium_free(sc64); sodium_free(sc); sodium_free(p3); sodium_free(p2);