From c15b76ef9b4b34f32721a682ed4fb01f1d46c60f Mon Sep 17 00:00:00 2001 From: Loup Vaillant Date: Thu, 12 Jan 2023 18:15:27 +0100 Subject: [PATCH] Added Ed25519ph Not sure this is such a good idea, considering how niche Ed25519ph is. Yet someone recently paid me handsomely for the functionality, sparking the recent overhaul of the EdDSA API that made it simpler and more flexible than ever. And now implementing Ed25519ph has become so cheap that I felt like thanking them by adding it upstream. --- src/optional/monocypher-ed25519.c | 44 +++++++++++++++++++++++++------ src/optional/monocypher-ed25519.h | 8 ++++++ tests/gen/makefile | 2 ++ tests/gen/vectors/ed_25519ph | 9 +++++++ tests/test.c | 32 ++++++++++++++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 tests/gen/vectors/ed_25519ph diff --git a/src/optional/monocypher-ed25519.c b/src/optional/monocypher-ed25519.c index 4974646..d175fb6 100644 --- a/src/optional/monocypher-ed25519.c +++ b/src/optional/monocypher-ed25519.c @@ -354,7 +354,8 @@ void crypto_ed25519_key_pair(u8 secret_key[64], u8 public_key[32], u8 seed[32]) static void hash_reduce(u8 h[32], const u8 *a, size_t a_size, const u8 *b, size_t b_size, - const u8 *c, size_t c_size) + const u8 *c, size_t c_size, + const u8 *d, size_t d_size) { u8 hash[64]; crypto_sha512_ctx ctx; @@ -362,23 +363,26 @@ static void hash_reduce(u8 h[32], crypto_sha512_update(&ctx, a, a_size); crypto_sha512_update(&ctx, b, b_size); crypto_sha512_update(&ctx, c, c_size); + crypto_sha512_update(&ctx, d, d_size); crypto_sha512_final (&ctx, hash); crypto_eddsa_reduce(h, hash); } -void crypto_ed25519_sign(u8 signature [64], const u8 secret_key[32], - const u8 *message, size_t message_size) +static void ed25519_dom_sign(u8 signature [64], const u8 secret_key[32], + const u8 *dom, size_t dom_size, + const u8 *message, size_t message_size) { u8 a[64]; // secret scalar and prefix u8 r[32]; // secret deterministic "random" nonce u8 h[32]; // publically verifiable hash of the message (not wiped) u8 R[32]; // first half of the signature (allows overlapping inputs) + const u8 *pk = secret_key + 32; crypto_sha512(a, secret_key, 32); crypto_eddsa_trim_scalar(a, a); - hash_reduce(r, a + 32, 32, message, message_size, 0, 0); + hash_reduce(r, dom, dom_size, a + 32, 32, message, message_size, 0, 0); crypto_eddsa_scalarbase(R, r); - hash_reduce(h, R, 32, secret_key + 32, 32, message, message_size); + hash_reduce(h, dom, dom_size, R, 32, pk, 32, message, message_size); COPY(signature, R, 32); crypto_eddsa_mul_add(signature + 32, h, a, r); @@ -386,14 +390,38 @@ void crypto_ed25519_sign(u8 signature [64], const u8 secret_key[32], WIPE_BUFFER(r); } -int crypto_ed25519_check(const u8 signature[64], const u8 public_key[32], +void crypto_ed25519_sign(u8 signature [64], const u8 secret_key[32], const u8 *message, size_t message_size) { - u8 h_ram [32]; - hash_reduce(h_ram, signature, 32, public_key, 32, message, message_size); + ed25519_dom_sign(signature, secret_key, 0, 0, message, message_size); +} + +int crypto_ed25519_check(const u8 signature[64], const u8 public_key[32], + const u8 *msg, size_t msg_size) +{ + u8 h_ram[32]; + hash_reduce(h_ram, signature, 32, public_key, 32, msg, msg_size, 0, 0); return crypto_eddsa_check_equation(signature, public_key, h_ram); } +static const u8 domain[34] = "SigEd25519 no Ed25519 collisions\1\0"; + +void crypto_ed25519_ph_sign(uint8_t signature[64], const uint8_t secret_key[32], + const uint8_t message_hash[64]) +{ + ed25519_dom_sign(signature, secret_key, domain, sizeof(domain), + message_hash, 64); +} + +int crypto_ed25519_ph_check(const uint8_t sig[64], const uint8_t pk[32], + const uint8_t msg_hash[64]) +{ + u8 h_ram[32]; + hash_reduce(h_ram, domain, sizeof(domain), sig, 32, pk, 32, msg_hash, 64); + return crypto_eddsa_check_equation(sig, pk, h_ram); +} + + #ifdef MONOCYPHER_CPP_NAMESPACE } #endif diff --git a/src/optional/monocypher-ed25519.h b/src/optional/monocypher-ed25519.h index 2f7b414..77bb6b9 100644 --- a/src/optional/monocypher-ed25519.h +++ b/src/optional/monocypher-ed25519.h @@ -117,6 +117,14 @@ int crypto_ed25519_check(const uint8_t signature [64], const uint8_t public_key[32], const uint8_t *message, size_t message_size); +// Pre-hash variants +void crypto_ed25519_ph_sign(uint8_t signature [64], + const uint8_t secret_key [32], + const uint8_t message_hash[64]); +int crypto_ed25519_ph_check(const uint8_t signature [64], + const uint8_t public_key [32], + const uint8_t message_hash[64]); + #ifdef __cplusplus } #endif diff --git a/tests/gen/makefile b/tests/gen/makefile index 5ab2852..be92c3d 100644 --- a/tests/gen/makefile +++ b/tests/gen/makefile @@ -58,6 +58,7 @@ VEC = chacha20 hchacha20 xchacha20 ietf_chacha20 \ aead_ietf aead_8439 \ poly1305 blake2b sha512 hmac_sha512 argon2 \ edDSA edDSA_pk ed_25519 ed_25519_pk ed_25519_check \ + ed_25519ph \ x25519 x25519_pk elligator_inv elligator_dir VEC2 = $(patsubst %, %.all.vec, $(VEC)) HEADERS = $(patsubst %, %.h.vec , $(VEC)) @@ -124,6 +125,7 @@ edDSA_pk.all.vec : edDSA_pk.vec ed_25519.all.vec : ed_25519.vec ed_25519_pk.all.vec : ed_25519_pk.vec ed_25519_check.all.vec: vectors/ed_25519_check +ed_25519ph.all.vec : vectors/ed_25519ph elligator_dir.all.vec : elligator_dir.vec vectors/elligator_dir elligator_inv.all.vec : elligator_inv.vec vectors/elligator_inv $(VEC2): diff --git a/tests/gen/vectors/ed_25519ph b/tests/gen/vectors/ed_25519ph new file mode 100644 index 0000000..29ab550 --- /dev/null +++ b/tests/gen/vectors/ed_25519ph @@ -0,0 +1,9 @@ +833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42: +ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf: +616263: +98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406: + +17A4AB24CACC54F1ED6AADBDBE0B22328C4F4F9747F75DCEC653796963367476: +b73f21f54921d332fcb8a32292c65bdafc91358d5383a3145e9aea57c9381440: +0000000000000000000000000000000000000000000000000000000000000000: +a8dd09bd36d6588a4535a3e258cd5bb75cef484f8f66bd7631b45bee89fddf03933ea8ea1c2b5636d133b7c5bb63f1ca55251b963cf3c9c13fe43bd773cc8600: diff --git a/tests/test.c b/tests/test.c index abe15e8..60e67ba 100644 --- a/tests/test.c +++ b/tests/test.c @@ -928,11 +928,43 @@ static void ed_25519_check(vector_reader *reader) msg.buf, msg.size); } +static void ed_25519ph(vector_reader *reader) +{ + vector sk = next_input(reader); + vector pk = next_input(reader); + vector msg = next_input(reader); + vector out = next_output(reader); + + // Test that we generate the correct public key + uint8_t secret_key[64]; + uint8_t public_key[32]; + crypto_ed25519_key_pair(secret_key, public_key, sk.buf); + ASSERT_EQUAL(public_key, pk.buf, 32); + ASSERT_EQUAL(public_key, secret_key + 32, 32); + + // Generate output signature for comparison + uint8_t digest[64]; + crypto_sha512(digest, msg.buf, msg.size); + crypto_ed25519_ph_sign(out.buf, secret_key, digest); + + // Test that the correct signature is accepted + ASSERT_OK(crypto_ed25519_ph_check(out.buf, pk.buf, digest)); + + // Test that corrupted signatures are rejected + for (size_t i = 0; i < 64; i++) { + uint8_t corrupt_signature[64]; + memcpy(corrupt_signature, out.buf, 64); + corrupt_signature[i] ^= 1; // corrupt one bit + ASSERT_KO(crypto_ed25519_ph_check(corrupt_signature, pk.buf, digest)); + } +} + static void test_ed25519() { VECTORS(ed_25519); VECTORS(ed_25519_pk); VECTORS(ed_25519_check); + VECTORS(ed_25519ph); } ///////////////// -- 2.47.3