Decaf is a point compression method that builds a prime-order group for (twisted) Edwards curves and Montgomery curves with cofactor $h = 4$ based on the Jacobi quartic [H2015]. The promise is to eliminate the cofactor when only operating on points decoded from Decaf. Ristretto then extends this approach to curves with cofactor $h = 8$[HVLA20].
However, I'm still not clear on whether scalars when using ristretto255 with Curve25519/Ed25519 still require clamping. Clamping does three things:
- It clears the lower three bits of the first byte, i.e. the lower three bits of the scalar to multiply with base point $B$. This is presumably there to clear the cofactor. However, it's unclear to me from [H2015] and [HVLA20] whether scalar multiplication needs the cofactor cleared in the first place or whether points with low-order components onto the prime-order group are “valid” input for Ristretto (and/or Decaf) or whether I should be clearing the cofactor for scalar multiplication. Experimental testing has shown that points with low-order components interoperate properly, but that means little regarding security.
- It clears the top bit of the last byte, i.e. bit 255 of the scalar. This is presumably there to always be in range of a valid scalar (since $8\ell$ = 0x80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f68 for Curve25519).
- It sets bit 6 of the last byte, i.e. sets bit 254 of the scalar (so that the scalar is always at least $2^{254}$). Going by [BJLS2015], this is there to thwart kangaroo attacks. However, [BL2013.Twist] notes that kangaroo attacks can be stopped by rejecting any point $P$ for which $hQ = 0$ holds (but that implementations would be likely to forget it, which may likely the reason for the inclusion in Ed25519/Curve25519), or having cofactor $h = 1$, i.e. having the order be prime.
So as far as I can tell, assuming that Ristretto (and Decaf) create prime-order groups and the base point is on the prime-order group, none of this bit twiddling would be required for any scalar multiplication. However, in spite of this, the Ed448-Goldilocks code using Decaf/Ristretto does the complete clamping procedure with just the comment /* Blarg */
: https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/per_curve/eddsa.tmpl.c#l36 (Note that according to HISTORY.txt
, it uses Ristretto, despite having decaf
in function names)
This may be the case because the same code likely does the batched version of Decaf introduced in [H2015], which re-introduces a cofactor $h=2$, going by the power. However, even with that, I do not see the reason to clamp by the full cofactor $h=4$ or $h=8$.
Thus my question: Do I need to perform the full clamping procedure with Ristretto and/or Decaf for Ed25519 and Ed448, can it be skipped entirely, do only parts of it need to be performed or is an entirely different procedure necessary?
Implementation overview
I went and scoured the other implementations of Ristretto as well.
- Go github.com/gtank/ristretto255:
scalar/scalar.go
reduces modulo Edwards25519 $\ell$. Opaque look-up tables make it difficult to see what is actually going on afterwards. - Rust curve25519-dalek: The
Scalar
type reduces scalars modulo Edwards25519 $\ell$ before scalar multiplication, seescalar.rs
andconstants.rs
in various subdirectories. Opaque look-up tables make it difficult to see what is actually going on afterwards. - C libsodium:
core_ristretto255.c
generates random scalars the same as for Ed25519 incore_ed25519.c
(which clears the top bit, but neither sets the high bit nor clears the low ones), selecting a scalar $S$ candidate (after truncating the top three bits, no doubt to try to get it maybe below Edwards25519 $\ell$) if it is in the range $0 < S < \ell$.crypto_scalarmult_ristretto255_base()
andcrypto_scalarmult_ristretto255()
inscalarmult_ristretto255_ref10.c
clear the top bit, but do not set the high bits and do not clear the low bits. This scalar is then immediately used for a scalar multiplication without clamping. - JavaScript ristretto255-js: Does not do any clamping.
getRandomScalar()
clips the top bit, but otherwise just checks a scalar candidate $S$ for $0 \le S < \ell$. NeitherscalarMult()
norscalarMultBase()
set the high bit or clear the low bits before passing it off to tweetnacl-js scalarmult, which does perform the clamping incrypto_scalarmult
(and apparently operates in Montgomery space anyway?). It has test vectors to test against curve25519-dalek though. - WebAssembly/TypeScript wasm-crypto: Makes its intention very clear. The only difference between
_signEdKeypairFromSeed()
and_signKeypairFromSeed
is the former callingscClamp()
before doing the scalar multiplication.
Results remain inconclusive, but seem to strongly hint towards not clamping. However, that's just a heuristic based on how people implemented it, rather than what is actually correct, and as such not any kind of canonical answer.
References
BJLS2015: Daniel J. Bernstein, Simon Joseffson, Tanja Lange, Peter Schwabe, Bo-Yin Yang. EdDSA for more curves, https://ed25519.cr.yp.to/eddsa-20150704.pdf
BL2013.Twist: Daniel J. Bernstein, Tanja Lange. SafeCurves: choosing safe curves for elliptic-curve cryptography: Twist security, https://safecurves.cr.yp.to/twist.html
H2015: Mike Hamburg. Decaf: Eliminating cofactors through point compression, https://www.shiftleft.org/papers/decaf/decaf.pdf
HVLA2020: Mike Hamburg, Henry de Valence, Isis Lovecruft, Tony Arcieri et al. The Ristretto Group, https://ristretto.group/ristretto.html