Document anti-malleability features/functionality.

This commit is contained in:
Isis Lovecruft 2019-10-07 19:03:15 +00:00
parent ce2260afab
commit 2d5fe86f30
No known key found for this signature in database
GPG key ID: AB41313533E8E812
2 changed files with 88 additions and 3 deletions

View file

@ -108,9 +108,45 @@ after the fact, breaking compatibility with every other implementation.
In short, if malleable signatures are bad for your protocol, don't use them.
Consider using a curve25519-based Verifiable Random Function (VRF), such as
[Trevor Perrin's VXEdDSA](https://www.whispersystems.org/docs/specifications/xeddsa/),
instead. We
[plan](https://github.com/dalek-cryptography/curve25519-dalek/issues/9) to
eventually support VXEdDSA in curve25519-dalek.
instead.
#### The `legacy_compatibility` Feature
By default, this library performs a stricter check for malleability in the
scalar component of a signature, upon signature deserialisation. This stricter
check, that `s < \ell` where `\ell` is the order of the basepoint, is
[mandated by RFC8032](https://tools.ietf.org/html/rfc8032#section-5.1.7).
However, that RFC was standardised a decade after the original paper, which, as
described above, (usually, falsely) stated that malleability was inconsequential.
Because of this, most ed25519 implementations only perform a limited, hackier
check that the most significant three bits of the scalar are unset. If you need
compatibility with legacy implementations, including:
* ed25519-donna
* Golang's /x/crypto ed25519
* libsodium (only when built with `-DED25519_COMPAT`)
* NaCl's "ref" implementation
* probably a bunch of others
then enable `ed25519-dalek`'s `legacy_compatibility` feature. Please note and
be forewarned that doing so allows for signature malleability, meaning that
there may be two different and "valid" signatures with the same key for the same
message, which is obviously incredibly dangerous in a number of contexts,
including—but not limited to—identification protocols and cryptocurrency
transactions.
#### The `verify_strict()` Function
The scalar component of a signature is not the only source of signature
malleability, however. Both the public key used for signature verification and
the group element component of the signature are malleable, as they may contain
a small torsion component as a consquence of the curve25519 group not being of
prime order, but having a small cofactor of 8.
If you wish to also eliminate this source of signature malleability, please
review the
[documentation for the `verify_strict()` function](https://doc.dalek.rs/ed25519_dalek/struct.PublicKey.html#method.verify_strict).
# Installation

View file

@ -108,6 +108,55 @@ impl Signature {
}
/// Construct a `Signature` from a slice of bytes.
///
/// # Scalar Malleability Checking
///
/// As originally specified in the ed25519 paper (cf. the "Malleability"
/// section of the README in this repo), no checks whatsoever were performed
/// for signature malleability.
///
/// Later, a semi-functional, hacky check was added to most libraries to
/// "ensure" that the scalar portion, `s`, of the signature was reduced `mod
/// \ell`, the order of the basepoint:
///
/// ```ignore
/// if signature.s[31] & 224 != 0 {
/// return Err();
/// }
/// ```
///
/// This bit-twiddling ensures that the most significant three bits of the
/// scalar are not set:
///
/// ```python,ignore
/// >>> 0b00010000 & 224
/// 0
/// >>> 0b00100000 & 224
/// 32
/// >>> 0b01000000 & 224
/// 64
/// >>> 0b10000000 & 224
/// 128
/// ```
///
/// However, this check is hacky and insufficient to check that the scalar is
/// fully reduced `mod \ell = 2^252 + 27742317777372353535851937790883648493` as
/// it leaves us with a guanteed bound of 253 bits. This means that there are
/// `2^253 - 2^252 + 2774231777737235353585193779088364849311` remaining scalars
/// which could cause malleabilllity.
///
/// RFC8032 [states](https://tools.ietf.org/html/rfc8032#section-5.1.7):
///
/// > To verify a signature on a message M using public key A, [...]
/// > first split the signature into two 32-octet halves. Decode the first
/// > half as a point R, and the second half as an integer S, in the range
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
/// > decodings fail (including S being out of range), the signature is
/// > invalid.
///
/// However, by the time this was standardised, most libraries in use were
/// only checking the most significant three bits. (See also the
/// documentation for `PublicKey.verify_strict`.)
#[inline]
pub fn from_bytes(bytes: &[u8]) -> Result<Signature, SignatureError> {
if bytes.len() != SIGNATURE_LENGTH {