EdDSA has more corner cases than we would like. Up until now we didn't
pay much attention.
- The first version of Monocypher didn't check the range of S, allowing
attackers to generate valid variants of existing signatures. While it
doesn't affect the core properties of signatures, some systems rely on
a stricter security guarantee: generating a new, distinct signature
must require the private key.
- When the public key has a low-order component, there can be an
inconsistency between various verification methods. Detecting such
keys is prohibitively expensive (a full scalar multiplication), and
some systems nevertheless require that everyone agrees whether a
signature is valid or not (if they don't we risk various failures such
as network partitions).
- Further disagreement can occur if A and R use a non-canonical
encoding, though in practice this only happens when the public key has
low order (and detecting _that_ is not expensive).
There is a wide consensus that the range of S should be checked, and we
do. Where consensus is lacking is with respect to the verification
method (batch or strict equation), checking for non-canonical encodings,
and checking that A has low order.
The current version is as permissive as the consensus allows:
- It checks the range of S.
- It uses the batch equation.
- It allows non-canonical encodings for A and R.
- It allows A to have low order.
The previous version on the other hand used the strict equation, and did
not allow non-canonical encodings for R. The reasons for the current
policy are as follows:
- Everyone checks the range of S, it provides an additional security
guarantee, and it makes verification slightly faster.
- The batch equation is the only one that is consistent with batched
verification. Batch verification is important because it allows up to
2x performance gains, precisely in settings where it might be the
bottleneck (performing many verifications).
- Allowing non-canonical encodings and low order A makes the code
simpler, and makes sure we do not start rejecting signatures that were
previously accepted.
- Though these choices aren't completely RFC 8032 compliant, they _are_
consistent with at least one library out there (Zebra). Note that if
we forbade low order A, we would be consistent with Libsodium instead.
Which library we chose to be consistent with is kind of arbitrary.
The main downside for now is an 8% drop in performance. 1% can be
recovered by replacing the 3 final doublings by comparisons, but 7% come
from R decompression, which is a necessary cost of the batch equation.
I hope to overcome this loss with a lattice based optimisation [Thomas
Pornin 2020].