AtlatestRepositorysigil-crypto

sigil-crypto / tree / nativeed25519-verify.c

1/*
2 * Ed25519 signature verification core for sigil-crypto.
3 *
4 * Pure C99 over the vendored Monocypher 4.0.3: no Sigil runtime, no
5 * allocation, no platform calls. Kept apart from native/ed25519.c (the
6 * Sigil natives) so it can be linked into a plain C harness and compared
7 * against libsodium (test/differential/).
8 */
9
10#include <string.h>
12#include "monocypher.h"
13#include "monocypher-ed25519.h"
15#include "ed25519-verify.h"
17/*
18 * RFC 8032 section 5.1.3 step 1: the low 255 bits of an encoded point are
19 * y, and "if the y-coordinate is >= p, decoding fails". p = 2^255 - 19, so
20 * y >= p exactly when the encoding (sign bit masked) is one of
21 * ed ff .. ff 7f, ee ff .. ff 7f, ..., ff ff .. ff 7f (little-endian).
22 * Monocypher reduces y mod p instead, so this check is ours.
23 */
24int sigil_crypto_ed25519_point_is_canonical(const unsigned char p[32])
26 if ((p[31] & 0x7f) != 0x7f) return 1;
27 for (int i = 30; i >= 1; i--) {
28 if (p[i] != 0xff) return 1;
29 }
30 return p[0] < 0xed;
33/*
34 * True when the encoded point decodes and has order dividing 8.
35 *
36 * Uses Monocypher's public crypto_eddsa_check_equation, which returns 0
37 * exactly when the encodings decode and [8]([s]B - [h]A - R) is the
38 * identity. With s = 0, h = 0 and A = the identity, that is [8](-R) = 0,
39 * i.e. R has small order. No curve arithmetic is done here.
40 *
41 * RFC 8032 section 5.1.3 step 4 ("if x = 0 and x_0 = 1, decoding fails")
42 * only concerns y = 1 and y = p - 1, the points of order 1 and 2; both are
43 * rejected by this test, so step 4 needs no separate check.
44 */
45int sigil_crypto_ed25519_has_small_order(const unsigned char p[32])
47 static const unsigned char identity[32] = {1};
48 static const unsigned char zero[32] = {0};
49 unsigned char sig[64];
50 memcpy(sig, p, 32);
51 memset(sig + 32, 0, 32);
52 return crypto_eddsa_check_equation(sig, identity, zero) == 0;
55int sigil_crypto_ed25519_verify(const unsigned char public_key[32],
56 const unsigned char *message,
57 size_t message_len,
58 const unsigned char signature[64])
60 /* Encodings first: A and R must be canonical and not of small order.
61 * These are the checks libsodium's crypto_sign_verify_detached (which
62 * minisign uses) makes before the equation. S < L is checked inside
63 * crypto_ed25519_check. */
64 if (!sigil_crypto_ed25519_point_is_canonical(public_key) ||
65 !sigil_crypto_ed25519_point_is_canonical(signature))
66 return 0;
67 if (sigil_crypto_ed25519_has_small_order(public_key) ||
68 sigil_crypto_ed25519_has_small_order(signature))
69 return 0;
70 return crypto_ed25519_check(signature, public_key,
71 message, message_len) == 0;