Repository navigation
Conversation
OpenSSL loads an Ed25519 public key whose point is of small order, and with the neutral point as key it verifies the signature R = the neutral point, S = 0 for any input. Without ext-sodium, AbstractEdDSA::verify() goes through OKPKey::verifyWithOpenSSL(), so Ed25519 and EdDSA accepted such a forged signature. With sodium, sodium_crypto_sign_verify_detached() refuses it. OKPKey::isSmallOrderEd25519Point() compares "x", sign bit cleared, with the 7 encodings libsodium refuses (ge25519_has_small_order()): the 8 points of order 1, 2, 4 and 8 and the non-canonical encodings of y = 0 and y = 1. It needs no extension. verifyWithOpenSSL() returns false for such a key, and OKPKeyAnalyzer reports it whichever backend is loaded.
OKPKeyAnalyzer only checked the size of "x" and the points of small order, so an Ed25519 key whose "x" decodes to no point of the curve raised no message, while every signature verification with it fails. OKPKey::isValidEd25519PublicKey() uses sodium when it is loaded: sodium_crypto_sign_ed25519_pk_to_curve25519() also requires the point to be in the prime-order subgroup. Without sodium, OKPKey::isEd25519Point() decodes "x" as RFC 8032 section 5.1.3 does, with brick/math: y lower than p and (y^2 - 1) / (d y^2 + 1) a square modulo p. A point outside the prime-order subgroup is then only reported with sodium. The tests build the "x" of a well-formed Ed25519 key from the RFC 8037 key, as random bytes are a point of the curve only half of the time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Target branch: 4.3.x
Includes:
The defect
Both defects are in code that is on 4.3.x only and not released yet: the OpenSSL backend of the Edwards curves came with
2bd3f62.
AbstractEdDSA::verify()goes throughOKPKey::verifyWithOpenSSL(). OpenSSL loadsan Ed25519 public key whose point is of small order, and with the neutral point as key (
x=01 00…00) it verifiesthe signature R = the neutral point, S = 0 for any input.
Ed25519andEdDSAaccepted such a forged signature. Withsodium,
sodium_crypto_sign_verify_detached()refuses it. Only a key that its owner registered with such a point isconcerned: a generated key is never of small order.
OKPKeyAnalyzeronly checked the size ofxanddand thealg, so an Ed25519 key of small order,or whose
xdecodes to no point of the curve, raised no message.The fix
OKPKey::isSmallOrderEd25519Point()comparesx, sign bit cleared, with the 7 encodings libsodium refuses(
ge25519_has_small_order()): the 8 points of order 1, 2, 4 and 8 and the non-canonical encodings of y = 0 and y = 1.It needs no extension.
verifyWithOpenSSL()returns false for such a key.OKPKey::isValidEd25519PublicKey()usessodium_crypto_sign_ed25519_pk_to_curve25519()when sodium is loaded, whichalso requires the point to be in the prime-order subgroup. Without sodium,
OKPKey::isEd25519Point()decodesxasRFC 8032 section 5.1.3 does, with brick/math: y lower than p and (y² − 1) / (d·y² + 1) a square modulo p. A point
outside the prime-order subgroup is therefore reported with sodium only.
OKPKeyAnalyzerreports a key of small order withInvalid key. The point of the Ed25519 public key is of small order., and anxthat is no valid public key withInvalid key. The point is not a valid Ed25519 public key.Without sodium,
isEd25519Point()takes 0.7 ms with GMP and about 1.5 s in pure PHP (twomodPowon 255-bit numbers).Its input is bounded: the analyzer checks that
xis 32 bytes long before, andisEd25519Point()returns false for anyother length first.
Tests
OKPKeyTest: the 7 encodings and their sign-bit variants are of small order, the RFC 8037 key is not; the decoding ofx(neutral point, y = 0, y = p − 1, y = 2, y = p, x = 0 with the sign bit set, a short string); a mixed-order pointis valid without sodium only;
verifyWithOpenSSL()refuses the forged signature.FullySpecifiedEdDSATest:EdDSAandEd25519return false for the forged signature, whichever backend is loaded.OKPKeyAnalyzerTest: the messages for small-order points, for y = 2 and for the mixed-order point. The helper of thewell-formed keys takes the
xof the RFC 8037 key for Ed25519, as random bytes are a point of the curve only half ofthe time.
Without the fix, the new tests fail; without sodium, the
FullySpecifiedEdDSATestone shows the forged signatureaccepted. With
ghcr.io/spomky-labs/phpqa:8.4:--git-diff-lines --git-diff-base=origin/4.3.x): 82mutants, 82 killed, covered code MSI 100%.