diff --git a/CHANGELOG.md b/CHANGELOG.md index f1cc270..cbe9df9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] ### Security +- **The Ed25519 arithmetic that verifies package signatures has no undefined behaviour left on its tested paths.** The ref10 field and scalar code in `services/crypto/src` reduced limbs with `carry << 25` (and `<< 26`, `<< 21`) on signed values that are negative about half the time, packed bytes in `fe_tobytes()` with `int32_t` shifts that overflow, and computed `babs` in `ge.c`'s `select()` by shifting a negative `signed char`. All three are undefined in C, so a compiler is entitled to miscompile the verifier, and under `-fsanitize=undefined` five suites aborted (`test_crypto_ed25519_loworder`, `test_ed25519_contract`, `test_pkg_anchor_usable`, `test_pkg_trust_anchor`, `test_pkg_install_paths`). Each shift is now a multiplication by the same power of two, or a shift of the value cast to `uint32_t`, which is what two's-complement hardware computed anyway, so the results do not change. No output change was observed: the new `tests/test_ed25519_rfc8032_kat.c` pins key generation and signing to RFC 8032 section 7.1 byte for byte, and passes against both the old and new code at `-O3`. Before this change nothing pinned the signer's output; the package tests only round-tripped their own signatures. - **Build backends no longer hand configuration to a shell unchecked, and never run a truncated command.** All eleven backends under `backends/` built their `system()` lines with `snprintf` from the project's `src_dir`, `build_dir`, `install_dir`, toolchain and option values, quoted but unfiltered: a value carrying `"; touch pwned; "` ran as shell (reproduced against the CMake backend's `clean`), and `freertos_install`, `cargo_install` and `nuttx_install` formatted three `EOS_MAX_PATH` paths into a 1024-byte buffer, so a long path ran a cut-off line. Commands are now assembled through `EosShellCmd` (`core/include/eos/shell_cmd.h`): program text written in the tree is appended as text, every configuration value goes through `eos_shell_cmd_arg()` (double-quoted, refused if it carries a shell metacharacter or control character) or `eos_shell_cmd_word()` (unquoted, an allowlist of one word), and a line that does not fit or carries a refused value is refused with `EOS_ERR_INVALID` before anything runs. Option values are quoted, so a value with a space is one argument. The two rules are the ones `linux_security` reviewed and shipped, moved to `core` and used by both; `linux_security`'s own predicates delegate to them. `eos_shell_cmd_run()` reports a shell that could not be started, or a command the shell could not run at all (exit 127), as `EOS_ERR_SYSTEM` rather than as the `EOS_ERR_BUILD` a genuine build failure produces. `tests/test_shell_cmd.c` covers the rules, the builder, and every backend against a hostile directory. ### Changed diff --git a/services/crypto/src/ed25519_fe.c b/services/crypto/src/ed25519_fe.c index 2105eb7..ce8af4f 100644 --- a/services/crypto/src/ed25519_fe.c +++ b/services/crypto/src/ed25519_fe.c @@ -318,34 +318,34 @@ void fe_frombytes(fe h, const unsigned char *s) { carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; - h9 -= carry9 << 25; + h9 -= carry9 * ((int64_t) 1 << 25); carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; - h1 -= carry1 << 25; + h1 -= carry1 * ((int64_t) 1 << 25); carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; - h3 -= carry3 << 25; + h3 -= carry3 * ((int64_t) 1 << 25); carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; - h5 -= carry5 << 25; + h5 -= carry5 * ((int64_t) 1 << 25); carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; - h7 -= carry7 << 25; + h7 -= carry7 * ((int64_t) 1 << 25); carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int64_t) 1 << 26); carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; - h2 -= carry2 << 26; + h2 -= carry2 * ((int64_t) 1 << 26); carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int64_t) 1 << 26); carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; - h6 -= carry6 << 26; + h6 -= carry6 * ((int64_t) 1 << 26); carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; - h8 -= carry8 << 26; + h8 -= carry8 * ((int64_t) 1 << 26); h[0] = (int32_t) h0; h[1] = (int32_t) h1; @@ -711,46 +711,46 @@ void fe_mul(fe h, const fe f, const fe g) { carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int64_t) 1 << 26); carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int64_t) 1 << 26); carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; - h1 -= carry1 << 25; + h1 -= carry1 * ((int64_t) 1 << 25); carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; - h5 -= carry5 << 25; + h5 -= carry5 * ((int64_t) 1 << 25); carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; - h2 -= carry2 << 26; + h2 -= carry2 * ((int64_t) 1 << 26); carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; - h6 -= carry6 << 26; + h6 -= carry6 * ((int64_t) 1 << 26); carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; - h3 -= carry3 << 25; + h3 -= carry3 * ((int64_t) 1 << 25); carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; - h7 -= carry7 << 25; + h7 -= carry7 * ((int64_t) 1 << 25); carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int64_t) 1 << 26); carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; - h8 -= carry8 << 26; + h8 -= carry8 * ((int64_t) 1 << 26); carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; - h9 -= carry9 << 25; + h9 -= carry9 * ((int64_t) 1 << 25); carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int64_t) 1 << 26); h[0] = (int32_t) h0; h[1] = (int32_t) h1; @@ -808,17 +808,17 @@ void fe_mul121666(fe h, fe f) { int64_t carry8; int64_t carry9; - carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; - carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; - carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; - carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; - carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 * ((int64_t) 1 << 25); + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 * ((int64_t) 1 << 25); + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 * ((int64_t) 1 << 25); + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 * ((int64_t) 1 << 25); + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 * ((int64_t) 1 << 25); - carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; - carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; - carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; - carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; - carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 * ((int64_t) 1 << 26); + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 * ((int64_t) 1 << 26); + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 * ((int64_t) 1 << 26); + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 * ((int64_t) 1 << 26); + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 * ((int64_t) 1 << 26); h[0] = (int32_t) h0; h[1] = (int32_t) h1; @@ -1080,40 +1080,40 @@ void fe_sq(fe h, const fe f) { int64_t carry9; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int64_t) 1 << 26); carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int64_t) 1 << 26); carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; - h1 -= carry1 << 25; + h1 -= carry1 * ((int64_t) 1 << 25); carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; - h5 -= carry5 << 25; + h5 -= carry5 * ((int64_t) 1 << 25); carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; - h2 -= carry2 << 26; + h2 -= carry2 * ((int64_t) 1 << 26); carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; - h6 -= carry6 << 26; + h6 -= carry6 * ((int64_t) 1 << 26); carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; - h3 -= carry3 << 25; + h3 -= carry3 * ((int64_t) 1 << 25); carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; - h7 -= carry7 << 25; + h7 -= carry7 * ((int64_t) 1 << 25); carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int64_t) 1 << 26); carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; - h8 -= carry8 << 26; + h8 -= carry8 * ((int64_t) 1 << 26); carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; - h9 -= carry9 << 25; + h9 -= carry9 * ((int64_t) 1 << 25); carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int64_t) 1 << 26); h[0] = (int32_t) h0; h[1] = (int32_t) h1; h[2] = (int32_t) h2; @@ -1253,40 +1253,40 @@ void fe_sq2(fe h, const fe f) { h9 += h9; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int64_t) 1 << 26); carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int64_t) 1 << 26); carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; - h1 -= carry1 << 25; + h1 -= carry1 * ((int64_t) 1 << 25); carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; - h5 -= carry5 << 25; + h5 -= carry5 * ((int64_t) 1 << 25); carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; - h2 -= carry2 << 26; + h2 -= carry2 * ((int64_t) 1 << 26); carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; - h6 -= carry6 << 26; + h6 -= carry6 * ((int64_t) 1 << 26); carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; - h3 -= carry3 << 25; + h3 -= carry3 * ((int64_t) 1 << 25); carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; - h7 -= carry7 << 25; + h7 -= carry7 * ((int64_t) 1 << 25); carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int64_t) 1 << 26); carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; - h8 -= carry8 << 26; + h8 -= carry8 * ((int64_t) 1 << 26); carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; - h9 -= carry9 << 25; + h9 -= carry9 * ((int64_t) 1 << 25); carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int64_t) 1 << 26); h[0] = (int32_t) h0; h[1] = (int32_t) h1; h[2] = (int32_t) h2; @@ -1421,33 +1421,33 @@ void fe_tobytes(unsigned char *s, const fe h) { /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ carry0 = h0 >> 26; h1 += carry0; - h0 -= carry0 << 26; + h0 -= carry0 * ((int32_t) 1 << 26); carry1 = h1 >> 25; h2 += carry1; - h1 -= carry1 << 25; + h1 -= carry1 * ((int32_t) 1 << 25); carry2 = h2 >> 26; h3 += carry2; - h2 -= carry2 << 26; + h2 -= carry2 * ((int32_t) 1 << 26); carry3 = h3 >> 25; h4 += carry3; - h3 -= carry3 << 25; + h3 -= carry3 * ((int32_t) 1 << 25); carry4 = h4 >> 26; h5 += carry4; - h4 -= carry4 << 26; + h4 -= carry4 * ((int32_t) 1 << 26); carry5 = h5 >> 25; h6 += carry5; - h5 -= carry5 << 25; + h5 -= carry5 * ((int32_t) 1 << 25); carry6 = h6 >> 26; h7 += carry6; - h6 -= carry6 << 26; + h6 -= carry6 * ((int32_t) 1 << 26); carry7 = h7 >> 25; h8 += carry7; - h7 -= carry7 << 25; + h7 -= carry7 * ((int32_t) 1 << 25); carry8 = h8 >> 26; h9 += carry8; - h8 -= carry8 << 26; + h8 -= carry8 * ((int32_t) 1 << 26); carry9 = h9 >> 25; - h9 -= carry9 << 25; + h9 -= carry9 * ((int32_t) 1 << 25); /* h10 = carry9 */ /* @@ -1459,32 +1459,32 @@ void fe_tobytes(unsigned char *s, const fe h) { s[0] = (unsigned char) (h0 >> 0); s[1] = (unsigned char) (h0 >> 8); s[2] = (unsigned char) (h0 >> 16); - s[3] = (unsigned char) ((h0 >> 24) | (h1 << 2)); + s[3] = (unsigned char) ((h0 >> 24) | ((uint32_t) h1 << 2)); s[4] = (unsigned char) (h1 >> 6); s[5] = (unsigned char) (h1 >> 14); - s[6] = (unsigned char) ((h1 >> 22) | (h2 << 3)); + s[6] = (unsigned char) ((h1 >> 22) | ((uint32_t) h2 << 3)); s[7] = (unsigned char) (h2 >> 5); s[8] = (unsigned char) (h2 >> 13); - s[9] = (unsigned char) ((h2 >> 21) | (h3 << 5)); + s[9] = (unsigned char) ((h2 >> 21) | ((uint32_t) h3 << 5)); s[10] = (unsigned char) (h3 >> 3); s[11] = (unsigned char) (h3 >> 11); - s[12] = (unsigned char) ((h3 >> 19) | (h4 << 6)); + s[12] = (unsigned char) ((h3 >> 19) | ((uint32_t) h4 << 6)); s[13] = (unsigned char) (h4 >> 2); s[14] = (unsigned char) (h4 >> 10); s[15] = (unsigned char) (h4 >> 18); s[16] = (unsigned char) (h5 >> 0); s[17] = (unsigned char) (h5 >> 8); s[18] = (unsigned char) (h5 >> 16); - s[19] = (unsigned char) ((h5 >> 24) | (h6 << 1)); + s[19] = (unsigned char) ((h5 >> 24) | ((uint32_t) h6 << 1)); s[20] = (unsigned char) (h6 >> 7); s[21] = (unsigned char) (h6 >> 15); - s[22] = (unsigned char) ((h6 >> 23) | (h7 << 3)); + s[22] = (unsigned char) ((h6 >> 23) | ((uint32_t) h7 << 3)); s[23] = (unsigned char) (h7 >> 5); s[24] = (unsigned char) (h7 >> 13); - s[25] = (unsigned char) ((h7 >> 21) | (h8 << 4)); + s[25] = (unsigned char) ((h7 >> 21) | ((uint32_t) h8 << 4)); s[26] = (unsigned char) (h8 >> 4); s[27] = (unsigned char) (h8 >> 12); - s[28] = (unsigned char) ((h8 >> 20) | (h9 << 6)); + s[28] = (unsigned char) ((h8 >> 20) | ((uint32_t) h9 << 6)); s[29] = (unsigned char) (h9 >> 2); s[30] = (unsigned char) (h9 >> 10); s[31] = (unsigned char) (h9 >> 18); diff --git a/services/crypto/src/ed25519_ge.c b/services/crypto/src/ed25519_ge.c index 87c691b..0e63868 100644 --- a/services/crypto/src/ed25519_ge.c +++ b/services/crypto/src/ed25519_ge.c @@ -356,7 +356,7 @@ static void cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) { static void select(ge_precomp *t, int pos, signed char b) { ge_precomp minust; unsigned char bnegative = negative(b); - unsigned char babs = b - (((-bnegative) & b) << 1); + unsigned char babs = b - (((-bnegative) & b) * 2); fe_1(t->yplusx); fe_1(t->yminusx); fe_0(t->xy2d); diff --git a/services/crypto/src/ed25519_sc.c b/services/crypto/src/ed25519_sc.c index da2c357..6eced8e 100644 --- a/services/crypto/src/ed25519_sc.c +++ b/services/crypto/src/ed25519_sc.c @@ -127,37 +127,37 @@ void sc_reduce(unsigned char *s) { s18 = 0; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; - s12 -= carry12 << 21; + s12 -= carry12 * ((int64_t) 1 << 21); carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; - s14 -= carry14 << 21; + s14 -= carry14 * ((int64_t) 1 << 21); carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; - s16 -= carry16 << 21; + s16 -= carry16 * ((int64_t) 1 << 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; - s11 -= carry11 << 21; + s11 -= carry11 * ((int64_t) 1 << 21); carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; - s13 -= carry13 << 21; + s13 -= carry13 * ((int64_t) 1 << 21); carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; - s15 -= carry15 << 21; + s15 -= carry15 * ((int64_t) 1 << 21); s5 += s17 * 666643; s6 += s17 * 470296; s7 += s17 * 654183; @@ -202,40 +202,40 @@ void sc_reduce(unsigned char *s) { s12 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; - s0 -= carry0 << 21; + s0 -= carry0 * ((int64_t) 1 << 21); carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; - s2 -= carry2 << 21; + s2 -= carry2 * ((int64_t) 1 << 21); carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; - s4 -= carry4 << 21; + s4 -= carry4 * ((int64_t) 1 << 21); carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; - s1 -= carry1 << 21; + s1 -= carry1 * ((int64_t) 1 << 21); carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; - s3 -= carry3 << 21; + s3 -= carry3 * ((int64_t) 1 << 21); carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; - s5 -= carry5 << 21; + s5 -= carry5 * ((int64_t) 1 << 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; - s11 -= carry11 << 21; + s11 -= carry11 * ((int64_t) 1 << 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; @@ -245,40 +245,40 @@ void sc_reduce(unsigned char *s) { s12 = 0; carry0 = s0 >> 21; s1 += carry0; - s0 -= carry0 << 21; + s0 -= carry0 * ((int64_t) 1 << 21); carry1 = s1 >> 21; s2 += carry1; - s1 -= carry1 << 21; + s1 -= carry1 * ((int64_t) 1 << 21); carry2 = s2 >> 21; s3 += carry2; - s2 -= carry2 << 21; + s2 -= carry2 * ((int64_t) 1 << 21); carry3 = s3 >> 21; s4 += carry3; - s3 -= carry3 << 21; + s3 -= carry3 * ((int64_t) 1 << 21); carry4 = s4 >> 21; s5 += carry4; - s4 -= carry4 << 21; + s4 -= carry4 * ((int64_t) 1 << 21); carry5 = s5 >> 21; s6 += carry5; - s5 -= carry5 << 21; + s5 -= carry5 * ((int64_t) 1 << 21); carry6 = s6 >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry7 = s7 >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry8 = s8 >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry9 = s9 >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry10 = s10 >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); carry11 = s11 >> 21; s12 += carry11; - s11 -= carry11 << 21; + s11 -= carry11 * ((int64_t) 1 << 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; @@ -288,37 +288,37 @@ void sc_reduce(unsigned char *s) { s12 = 0; carry0 = s0 >> 21; s1 += carry0; - s0 -= carry0 << 21; + s0 -= carry0 * ((int64_t) 1 << 21); carry1 = s1 >> 21; s2 += carry1; - s1 -= carry1 << 21; + s1 -= carry1 * ((int64_t) 1 << 21); carry2 = s2 >> 21; s3 += carry2; - s2 -= carry2 << 21; + s2 -= carry2 * ((int64_t) 1 << 21); carry3 = s3 >> 21; s4 += carry3; - s3 -= carry3 << 21; + s3 -= carry3 * ((int64_t) 1 << 21); carry4 = s4 >> 21; s5 += carry4; - s4 -= carry4 << 21; + s4 -= carry4 * ((int64_t) 1 << 21); carry5 = s5 >> 21; s6 += carry5; - s5 -= carry5 << 21; + s5 -= carry5 * ((int64_t) 1 << 21); carry6 = s6 >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry7 = s7 >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry8 = s8 >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry9 = s9 >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry10 = s10 >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); s[0] = (unsigned char) (s0 >> 0); s[1] = (unsigned char) (s0 >> 8); @@ -494,73 +494,73 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, s23 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; - s0 -= carry0 << 21; + s0 -= carry0 * ((int64_t) 1 << 21); carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; - s2 -= carry2 << 21; + s2 -= carry2 * ((int64_t) 1 << 21); carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; - s4 -= carry4 << 21; + s4 -= carry4 * ((int64_t) 1 << 21); carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; - s12 -= carry12 << 21; + s12 -= carry12 * ((int64_t) 1 << 21); carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; - s14 -= carry14 << 21; + s14 -= carry14 * ((int64_t) 1 << 21); carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; - s16 -= carry16 << 21; + s16 -= carry16 * ((int64_t) 1 << 21); carry18 = (s18 + (1 << 20)) >> 21; s19 += carry18; - s18 -= carry18 << 21; + s18 -= carry18 * ((int64_t) 1 << 21); carry20 = (s20 + (1 << 20)) >> 21; s21 += carry20; - s20 -= carry20 << 21; + s20 -= carry20 * ((int64_t) 1 << 21); carry22 = (s22 + (1 << 20)) >> 21; s23 += carry22; - s22 -= carry22 << 21; + s22 -= carry22 * ((int64_t) 1 << 21); carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; - s1 -= carry1 << 21; + s1 -= carry1 * ((int64_t) 1 << 21); carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; - s3 -= carry3 << 21; + s3 -= carry3 * ((int64_t) 1 << 21); carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; - s5 -= carry5 << 21; + s5 -= carry5 * ((int64_t) 1 << 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; - s11 -= carry11 << 21; + s11 -= carry11 * ((int64_t) 1 << 21); carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; - s13 -= carry13 << 21; + s13 -= carry13 * ((int64_t) 1 << 21); carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; - s15 -= carry15 << 21; + s15 -= carry15 * ((int64_t) 1 << 21); carry17 = (s17 + (1 << 20)) >> 21; s18 += carry17; - s17 -= carry17 << 21; + s17 -= carry17 * ((int64_t) 1 << 21); carry19 = (s19 + (1 << 20)) >> 21; s20 += carry19; - s19 -= carry19 << 21; + s19 -= carry19 * ((int64_t) 1 << 21); carry21 = (s21 + (1 << 20)) >> 21; s22 += carry21; - s21 -= carry21 << 21; + s21 -= carry21 * ((int64_t) 1 << 21); s11 += s23 * 666643; s12 += s23 * 470296; s13 += s23 * 654183; @@ -605,37 +605,37 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, s18 = 0; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; - s12 -= carry12 << 21; + s12 -= carry12 * ((int64_t) 1 << 21); carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; - s14 -= carry14 << 21; + s14 -= carry14 * ((int64_t) 1 << 21); carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; - s16 -= carry16 << 21; + s16 -= carry16 * ((int64_t) 1 << 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; - s11 -= carry11 << 21; + s11 -= carry11 * ((int64_t) 1 << 21); carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; - s13 -= carry13 << 21; + s13 -= carry13 * ((int64_t) 1 << 21); carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; - s15 -= carry15 << 21; + s15 -= carry15 * ((int64_t) 1 << 21); s5 += s17 * 666643; s6 += s17 * 470296; s7 += s17 * 654183; @@ -680,40 +680,40 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, s12 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; - s0 -= carry0 << 21; + s0 -= carry0 * ((int64_t) 1 << 21); carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; - s2 -= carry2 << 21; + s2 -= carry2 * ((int64_t) 1 << 21); carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; - s4 -= carry4 << 21; + s4 -= carry4 * ((int64_t) 1 << 21); carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; - s1 -= carry1 << 21; + s1 -= carry1 * ((int64_t) 1 << 21); carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; - s3 -= carry3 << 21; + s3 -= carry3 * ((int64_t) 1 << 21); carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; - s5 -= carry5 << 21; + s5 -= carry5 * ((int64_t) 1 << 21); carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; - s11 -= carry11 << 21; + s11 -= carry11 * ((int64_t) 1 << 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; @@ -723,40 +723,40 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, s12 = 0; carry0 = s0 >> 21; s1 += carry0; - s0 -= carry0 << 21; + s0 -= carry0 * ((int64_t) 1 << 21); carry1 = s1 >> 21; s2 += carry1; - s1 -= carry1 << 21; + s1 -= carry1 * ((int64_t) 1 << 21); carry2 = s2 >> 21; s3 += carry2; - s2 -= carry2 << 21; + s2 -= carry2 * ((int64_t) 1 << 21); carry3 = s3 >> 21; s4 += carry3; - s3 -= carry3 << 21; + s3 -= carry3 * ((int64_t) 1 << 21); carry4 = s4 >> 21; s5 += carry4; - s4 -= carry4 << 21; + s4 -= carry4 * ((int64_t) 1 << 21); carry5 = s5 >> 21; s6 += carry5; - s5 -= carry5 << 21; + s5 -= carry5 * ((int64_t) 1 << 21); carry6 = s6 >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry7 = s7 >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry8 = s8 >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry9 = s9 >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry10 = s10 >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); carry11 = s11 >> 21; s12 += carry11; - s11 -= carry11 << 21; + s11 -= carry11 * ((int64_t) 1 << 21); s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; @@ -766,37 +766,37 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, s12 = 0; carry0 = s0 >> 21; s1 += carry0; - s0 -= carry0 << 21; + s0 -= carry0 * ((int64_t) 1 << 21); carry1 = s1 >> 21; s2 += carry1; - s1 -= carry1 << 21; + s1 -= carry1 * ((int64_t) 1 << 21); carry2 = s2 >> 21; s3 += carry2; - s2 -= carry2 << 21; + s2 -= carry2 * ((int64_t) 1 << 21); carry3 = s3 >> 21; s4 += carry3; - s3 -= carry3 << 21; + s3 -= carry3 * ((int64_t) 1 << 21); carry4 = s4 >> 21; s5 += carry4; - s4 -= carry4 << 21; + s4 -= carry4 * ((int64_t) 1 << 21); carry5 = s5 >> 21; s6 += carry5; - s5 -= carry5 << 21; + s5 -= carry5 * ((int64_t) 1 << 21); carry6 = s6 >> 21; s7 += carry6; - s6 -= carry6 << 21; + s6 -= carry6 * ((int64_t) 1 << 21); carry7 = s7 >> 21; s8 += carry7; - s7 -= carry7 << 21; + s7 -= carry7 * ((int64_t) 1 << 21); carry8 = s8 >> 21; s9 += carry8; - s8 -= carry8 << 21; + s8 -= carry8 * ((int64_t) 1 << 21); carry9 = s9 >> 21; s10 += carry9; - s9 -= carry9 << 21; + s9 -= carry9 * ((int64_t) 1 << 21); carry10 = s10 >> 21; s11 += carry10; - s10 -= carry10 << 21; + s10 -= carry10 * ((int64_t) 1 << 21); s[0] = (unsigned char) (s0 >> 0); s[1] = (unsigned char) (s0 >> 8); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8688b7c..97eae06 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -200,6 +200,16 @@ target_include_directories(test_ed25519_contract PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) add_test(NAME test_ed25519_contract COMMAND test_ed25519_contract) +# --- test_ed25519_rfc8032_kat: keygen and signing are bit-exact --- +# The contract corpus pins what the verifier accepts; this pins what the +# signer emits, so a change to the ref10 field/scalar arithmetic that broke +# both sides alike cannot round-trip its way to green. +add_executable(test_ed25519_rfc8032_kat test_ed25519_rfc8032_kat.c) +target_link_libraries(test_ed25519_rfc8032_kat PRIVATE eos_crypto) +target_include_directories(test_ed25519_rfc8032_kat PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}) +add_test(NAME test_ed25519_rfc8032_kat COMMAND test_ed25519_rfc8032_kat) + # --- test_crypto_sha512: SHA-512 against NIST vectors --- add_executable(test_crypto_sha512 test_crypto_sha512.c) target_link_libraries(test_crypto_sha512 PRIVATE eos_crypto) diff --git a/tests/test_ed25519_rfc8032_kat.c b/tests/test_ed25519_rfc8032_kat.c new file mode 100644 index 0000000..d990a2c --- /dev/null +++ b/tests/test_ed25519_rfc8032_kat.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 EoS Project + +/** + * @file test_ed25519_rfc8032_kat.c + * @brief Key generation and signing reproduce RFC 8032 section 7.1 exactly. + * + * test_ed25519_contract proves the verifier accepts the RFC 8032 signatures, + * and the package tests sign and then verify their own output. Neither pins + * what ed25519_create_keypair() and ed25519_sign() produce: a change that + * altered the field or scalar arithmetic on both sides alike would still + * round-trip. Ed25519 is deterministic, so the exact bytes are checkable. + * + * This matters because the ref10 arithmetic under services/crypto/src used to + * left-shift negative signed values and overflow int32_t when packing bytes -- + * undefined behaviour a compiler may miscompile. Those were rewritten as + * multiplications and unsigned shifts; this test is what says the rewrite + * produced the same numbers. Run it under -fsanitize=undefined as well, as the + * nightly Sanitizers job does, to say there is no UB left on these paths. + * + * Public keys, messages and signatures come from the shared contract corpus, + * so they are not duplicated here. The corpus carries no secrets, so the three + * seeds are RFC 8032's own, checked against that corpus with an independent + * implementation before being committed. + */ + +#include "ed25519.h" + +#include "vectors/ed25519_contract_vectors.h" + +#include +#include + +typedef struct { + const char *name; /* entry in eos_ed25519_contract_vectors */ + unsigned char seed[32]; /* RFC 8032 section 7.1 SECRET KEY */ +} rfc8032_seed_t; + +static const rfc8032_seed_t seeds[] = { + { "rfc8032_test1_empty", + {0x9d,0x61,0xb1,0x9d,0xef,0xfd,0x5a,0x60,0xba,0x84,0x4a,0xf4,0x92,0xec,0x2c,0xc4, + 0x44,0x49,0xc5,0x69,0x7b,0x32,0x69,0x19,0x70,0x3b,0xac,0x03,0x1c,0xae,0x7f,0x60} }, + { "rfc8032_test2_1byte", + {0x4c,0xcd,0x08,0x9b,0x28,0xff,0x96,0xda,0x9d,0xb6,0xc3,0x46,0xec,0x11,0x4e,0x0f, + 0x5b,0x8a,0x31,0x9f,0x35,0xab,0xa6,0x24,0xda,0x8c,0xf6,0xed,0x4f,0xb8,0xa6,0xfb} }, + { "rfc8032_test3_2byte", + {0xc5,0xaa,0x8d,0xf4,0x3f,0x9f,0x83,0x7b,0xed,0xb7,0x44,0x2f,0x31,0xdc,0xb7,0xb1, + 0x66,0xd3,0x85,0x35,0x07,0x6f,0x09,0x4b,0x85,0xce,0x3a,0x2e,0x0b,0x44,0x58,0xf7} }, +}; + +static const eos_ed25519_contract_vector_t *find_vector(const char *name) { + for (int i = 0; i < EOS_ED25519_CONTRACT_COUNT; i++) { + if (strcmp(eos_ed25519_contract_vectors[i].name, name) == 0) + return &eos_ed25519_contract_vectors[i]; + } + return NULL; +} + +static void print_hex(const char *label, const unsigned char *p, size_t n) { + printf(" %-9s ", label); + for (size_t i = 0; i < n; i++) printf("%02x", p[i]); + printf("\n"); +} + +int main(void) { + unsigned failed = 0; + const size_t count = sizeof seeds / sizeof seeds[0]; + + printf("=== Ed25519 RFC 8032 known answers (keygen + sign) ===\n"); + + for (size_t i = 0; i < count; i++) { + const eos_ed25519_contract_vector_t *v = find_vector(seeds[i].name); + unsigned char pub[32], priv[64], sig[64]; + + printf(" %-28s ", seeds[i].name); + + /* The corpus is generated; if an entry is renamed or dropped, say so + * rather than silently testing fewer vectors. */ + if (!v || !v->expect_accept) { + printf("[FAIL] not an accept vector in the contract corpus\n"); + failed++; + continue; + } + + ed25519_create_keypair(pub, priv, seeds[i].seed); + if (memcmp(pub, v->public_key, sizeof pub) != 0) { + printf("[FAIL] public key\n"); + print_hex("expected", v->public_key, sizeof pub); + print_hex("got", pub, sizeof pub); + failed++; + continue; + } + + ed25519_sign(sig, v->message, v->message_len, pub, priv); + if (memcmp(sig, v->signature, sizeof sig) != 0) { + printf("[FAIL] signature\n"); + print_hex("expected", v->signature, sizeof sig); + print_hex("got", sig, sizeof sig); + failed++; + continue; + } + + /* Close the loop through the verifier on the bytes just produced. */ + if (ed25519_verify(sig, v->message, v->message_len, pub) != 1) { + printf("[FAIL] own signature refused\n"); + failed++; + continue; + } + + printf("[PASS]\n"); + } + + if (failed) { + printf("[FAIL] %u of %zu RFC 8032 vectors\n", failed, count); + return 1; + } + printf("[PASS] all %zu RFC 8032 vectors: keygen and signing are bit-exact\n", count); + return 0; +}