diff --git a/src/cryptoconditions/src/include/ed25519/src/verify.c b/src/cryptoconditions/src/include/ed25519/src/verify.c index 32f988edc8b..c749d9b9919 100644 --- a/src/cryptoconditions/src/include/ed25519/src/verify.c +++ b/src/cryptoconditions/src/include/ed25519/src/verify.c @@ -3,6 +3,14 @@ #include "ge.h" #include "sc.h" +/* L = 2^252+27742317777372353535851937790883648493 in little-endian form */ +const unsigned char curve25519_order[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 +}; + static int consttime_equal(const unsigned char *x, const unsigned char *y) { unsigned char r = 0; @@ -59,6 +67,21 @@ int ed25519_verify(const unsigned char *signature, const unsigned char *message, return 0; } + /* make sure 0 <= s < L, as per RFC 8032, section 5.1.7 to prevent signature + * malleability. Due to the three-bit check above (forces s < 2^253) there + * is not that much room, but adding L once works with most signatures */ + for (int i = 31; ; i--) + { + if (signature[i+32] < curve25519_order[i]) + { + break; + } + else if (signature[i+32] > curve25519_order[i] || i == 0) + { + return 0; + } + } + sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); diff --git a/src/cryptoconditions/src/include/ed25519/test.c b/src/cryptoconditions/src/include/ed25519/test.c index e2159a9af82..363575c18bd 100644 --- a/src/cryptoconditions/src/include/ed25519/test.c +++ b/src/cryptoconditions/src/include/ed25519/test.c @@ -9,6 +9,17 @@ #include "src/ge.h" #include "src/sc.h" +void print_signature(unsigned char signature[64], char *msg) { + printf("%s\n", msg); + for (int i = 0; i < 64; i++) { + printf("%02x", signature[i]); + if ((i + 1) % 8 != 0) { + printf(" "); + } else { + printf("\n"); + } + } +} int main() { unsigned char public_key[32], private_key[64], seed[32], scalar[32]; @@ -146,5 +157,41 @@ int main() { printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + /* ed25519 signature malleability test */ + + printf("testing signature malleability\n"); + + const unsigned char curve25519_order[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 + }; + + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + ed25519_sign(signature, message, message_len, public_key, private_key); + + print_signature(signature, "Signature:"); + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + // add L to S, which starts at sig[32] + unsigned int s = 0; + for (size_t i = 0; i < 32; i++) { + s = signature[32 + i] + curve25519_order[i] + (s >> 8); + signature[32 + i] = s & 0xff; + } + + print_signature(signature,"Modified signature:"); + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + return 0; } diff --git a/src/cryptoconditions/test-requirements.txt b/src/cryptoconditions/test-requirements.txt index 3e7a933b91c..8dd61a78cf6 100644 --- a/src/cryptoconditions/test-requirements.txt +++ b/src/cryptoconditions/test-requirements.txt @@ -1,3 +1,3 @@ base58==0.2.5 secp256k1 -pytest +pytest>=8.0.0