-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
verify_strict
and weak-key forgery
#259
Comments
@rozbb if *I know this has been exhaustively studied but I'm not sure what might've changed in the interim since those blog posts and papers were written |
Libsodium does small order checks on But I don't think HACL does low-order checks. Go's ed25519 impl does not (and they also have a faulty scalar check). Go's NaCl just uses their ed25519 impl. And CIRCL doesn't do the checks. So I guess no one actually fixed this. I'm still pro keeping |
Ok, well it seems like some feature with roughly this shape is common in other libraries and thus probably worth supporting, albeit unfortunate they don't agree on what the actual validation rules are. |
Ok cool. There's a related thing I'll make a PR for: the |
Closing. We're keeping |
Documenting here: I think My reasoning, previously stated here, is that it you need to know |
Uuuuh I'd like to hear more about the scalar check? :) |
Hi Fillipo! I just looked into it and I think I was mistaken. What I saw was this line if len(sig) != SignatureSize || sig[63] & 224 != 0 {
return false
} This is the old libsodium scalar check, which only checks that the top 3 bits are unset, and leads to possible signature malleability. But I see now that you later call So nevermind, sorry about that! |
Sweet, thank you for checking! |
This issue comes after a long time thinking about what
verify_strict
does and whether we want to remove it. I argue (surprise!) that we should not.The
verify_strict
function checks that the verifying keyA
, andsig.R
are not low-order points.The original intent seems to have been a stronger check, namely checking torsion-freeness of those curve points, but that never happened afaict. These are very different checks.
Regardless, let's assume it is doing torsion-freeness checks. There is documentation in the README and in
verify_strict
claiming that permittingR
orA
with torsion (which still have large order) yields signature malleability. I don't think this is true though, as both theR
andA
components are included in the hashH(R || A || M)
, andA
is still large enough order that the change in the hash yields an entirely different challenge "in the exponent". The docs are discussed more here. There's a similar claim in this comment, but again I haven't seen an example yet.However! I think
verify_strict
is still valuable.The low-order check on
A
meansverify_strict
provides resistance to weak-key forgeries. In particular, ifA
is low-order, thenH(R || A || M) A == H(R || A || M') A
with 1/8 probability, for any distinct messagesM
andM'
. We have a demonstration of this in our tests. In terms of impact, Scuttlebutt had a weak-key forgery vuln due to precisely this issue (see here, section 7.1). The attack context is: the attacker can pick their pubkey and provide a signature but the message is not known to them. The attacker uses a weak pubkeyA
, generates ans
, letsR = sB
, and sends(A, σ=(s, R))
to the verifier. With 1/8 probability, the secret messageM
known to the verifier satisfiessB == R + H(R || A || M) A
. Note that the forgery method here can also be thought of as a malleability attack, since you can find plenty of(s, R=sB)
pairs that pass verification on(A, M)
by simple trial and error.Some things that aren't clear to me are whether there really is a malleability issue on large-order pubkeys with torsion, and whether the low-order check on
R
does anything for us. @isislovecruft if you could explain that'd be awesome.Going forward, we can do a few things:
verify_strict
and update the docs around it, explaining weak-key forgery and removing previous claims about malleability. According to the paper, Go's NaCl impl, Cloudflare's CIRCL, and Libsodium have all implemented low-order pubkey checks.verify_strict
actually do the torsion-freeness checks. There's actually a reason you might want this. If you did this in batch verif as well, then it would agree perfectly with single verif. This way you could have consistent batch verif that rejects weak-key forgeries.I like (1), especially since you can't really do a low-order check in our current API. IMO
verify_strict
should be the default functionality, but I don't think we can do that now.(3) sounds OK too, though allegedly the torsion-freeness check is pretty expensive and it might not make sense in a batch setting from the start.
cc @tarcieri
The text was updated successfully, but these errors were encountered: