mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
Document anti-malleability features/functionality.
This commit is contained in:
parent
ce2260afab
commit
2d5fe86f30
2 changed files with 88 additions and 3 deletions
42
README.md
42
README.md
|
|
@ -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.
|
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
|
Consider using a curve25519-based Verifiable Random Function (VRF), such as
|
||||||
[Trevor Perrin's VXEdDSA](https://www.whispersystems.org/docs/specifications/xeddsa/),
|
[Trevor Perrin's VXEdDSA](https://www.whispersystems.org/docs/specifications/xeddsa/),
|
||||||
instead. We
|
instead.
|
||||||
[plan](https://github.com/dalek-cryptography/curve25519-dalek/issues/9) to
|
|
||||||
eventually support VXEdDSA in curve25519-dalek.
|
#### 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
|
# Installation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,55 @@ impl Signature {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a `Signature` from a slice of bytes.
|
/// 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]
|
#[inline]
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Signature, SignatureError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Signature, SignatureError> {
|
||||||
if bytes.len() != SIGNATURE_LENGTH {
|
if bytes.len() != SIGNATURE_LENGTH {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue