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, no5
* allocation, no platform calls. Kept apart from native/ed25519.c (the6
* Sigil natives) so it can be linked into a plain C harness and compared7
* against libsodium (test/differential/).8
*/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 are19
* y, and "if the y-coordinate is >= p, decoding fails". p = 2^255 - 19, so20
* y >= p exactly when the encoding (sign bit masked) is one of21
* 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
*/24
int sigil_crypto_ed25519_point_is_canonical(const unsigned char p[32])25
{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;31
}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 037
* exactly when the encodings decode and [8]([s]B - [h]A - R) is the38
* 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 are43
* rejected by this test, so step 4 needs no separate check.44
*/45
int sigil_crypto_ed25519_has_small_order(const unsigned char p[32])46
{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;53
}55
int 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])59
{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 (which62
* minisign uses) makes before the equation. S < L is checked inside63
* 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;72
}