-
Notifications
You must be signed in to change notification settings - Fork 247
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
[SEC] Observations on Point Validation #1715
Comments
On G2, the cofactor is over 500 bits so checking signature is even worse # Parameters
x = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
r = x^4 - x^2 + 1
t = x + 1
print('p : ' + p.hex())
print('r : ' + r.hex())
print('t : ' + t.hex())
print('p (mod r) == t-1 (mod r) == 0x' + (p % r).hex())
# Finite fields
Fp = GF(p)
K2.<u> = PolynomialRing(Fp)
Fp2.<beta> = Fp.extension(u^2+1)
# Curves
b = 4
SNR = Fp2([1, 1])
G1 = EllipticCurve(Fp, [0, b])
G2 = EllipticCurve(Fp2, [0, b*SNR])
# https://crypto.stackexchange.com/questions/64064/order-of-twisted-curve-in-pairings
# https://math.stackexchange.com/questions/144194/how-to-find-the-order-of-elliptic-curve-over-finite-field-extension
cofactorG1 = G1.order() // r
cofactorG2 = G2.order() // r
print('')
print('cofactor G1: ' + cofactorG1.hex())
print('cofactor G2: ' + cofactorG2.hex())
print('cofactor G1 bitlength: ' + str(int(cofactorG1).bit_length()))
print('cofactor G2 bitlength: ' + str(int(cofactorG2).bit_length()))
print('')
ImplementationWith BLST backend, subgroup checks uses Bowe19 endomorphisms https://eprint.iacr.org/2019/814 With MIRACL we use naive multiplication by the cofactor but this backend is used as a fallback i.e. for our main targets x86-64 and ARM64 subgroup checks is cheap enough Bowe19 subgroup checksIn spec form they are pairingwg/bls_standard#21
AnalysisWith endomorphism acceleration, those subgroup checks becomes dominated by scalar multiplications |
Description
This is a strictly informational observation intended to generate thought on the overall point validation strategy.
The BLS Signature specification places a high importance on point validation as evidenced by the following excerpt:
The BLS Signature functionality implemented in
nim-blscurve
does in fact perform the validations described above. However, theparsePubkey()
function inbeacon_chain/validator_api.nim
shown below simply deserializes a hex string and passes it along to downstream logic without further (subgroup) validation.https://github.com/status-im/nim-beacon-chain/blob/4b38619c4f51847fc591e85327ed226ad46d24ac/beacon_chain/validator_api.nim#L36-L40
The above case itself is not a significant security issue, as the result is to be returned in responses for the
get_v1_beacon_states_stateId_validators
andget_v1_beacon_states_stateId_validators_validatorId
RPC endpoints which should be validated by the request originator prior to usage. Arguably, point validation at usage and ingestion is far more important than an output path.However, it is noted that the BLS signature functionality specification assumes data originates and terminates as byte strings (e.g., the
signature_to_point()
function is step 1 in theCoreVerify
,Aggregate
, andCoreAggregateVerify
algorithms, whilepoint_to_signature()
is essentially the last step in theCoreSign
,Aggregate
,PopProve
algorithms). In contrast, thenim-crypto
andnim-beacon-chain
implementation tends to deserialize points once, store them internally in point form, and pass these points to the individual algorithms.While there are a variety of places and paths in beacon chain to deserialize points, they all go through the lowest-level byte-oriented function (i.e., the
fromBytes()
function shown below). This function serves as a 'choke point' for G2 (E2) and there is a nearby sibling for G1 (E1). They currently do not perform subgroup validation.https://github.com/status-im/nim-blscurve/blob/da9ae49dab4cbb95b2cdaa68ecc221ee15c67a9a/blscurve/miracl/common.nim#L649-L674
In any event, ensuring all points are validated correctly prior to processing remains a constant challenge as the code evolves. Further, note that that this validation is expensive: point multiplication by the cofactor which is a 125-bit integer.
Exploit Scenario
The original
parsePubkey()
function cited above demonstrates the potential for missing validation checks. As stated in the referenced standard, the pairing operation is undefined when its input points are not in the prime-order subgroups of E1 and E2. The resulting behavior is unpredictable and may enable forgeries.Mitigation Recommendation
The recommendation is to consider placing the point (subgroup) validation check at the location of deserialization immediately after the code shown above. The two routines could perform point validation by default but skip validation as necessary when given an optional flag. The point structure could include a flag indicating the check has been performed, so that algorithms can confirm the check (rather than repeat it) or execute it in a lazy fashion (though this risks mutability issues). Alternatively, a validated point could be given a different type to cleanly separate/indicate its status, e.g.
unverifiedPubKey
andverifiedPubKey
. Overall, these approaches would allow the removal of a variety of potentially redundant and expensive checks in the BLS signature functionality and increase performance as well as robustly ensuring all points are validated prior to processing. Finally, detecting errors early at ingestion improves debug and attribution capabilities.The text was updated successfully, but these errors were encountered: