Add RFC 8032 and NIST full validation decompression to EdwardsPoint#833
Add RFC 8032 and NIST full validation decompression to EdwardsPoint#833daxpedda wants to merge 1 commit intodalek-cryptography:mainfrom
EdwardsPoint#833Conversation
aa58b98 to
897717e
Compare
|
|
897717e to
52877f1
Compare
|
As outlined in #626, there are effectively three profiles to consider:
I spitballed a few names in #626 but an important thing to consider in the API design is how to handle the three different cases in ways that make sense. This is all to say that We've spitballed whether different inherent methods or an enum make sense. I guess I'm generally leaning towards methods, maybe ones that include the actual validation criteria?
|
52877f1 to
e5bdfa1
Compare
|
That makes a lot of sense to me. |
e5bdfa1 to
bc379bc
Compare
|
Done! |
bc379bc to
29b771d
Compare
CompressedEdwardsY::validated_decompress()EdwardsPoint
29b771d to
456fbb6
Compare
nazar-pc
left a comment
There was a problem hiding this comment.
API-wise this makes sense and is backwards compatible, but my preference would be to not have the "default" implementation, but rather let the user to choose the actual variant explicitly.
For example, in ed25519-dalek there is VerifyingKey struct with from_bytes() method, which will currently uses ZIP-215 and with this PR it will not really be obvious that an alternative exists.
On another hand deserialization with serde will be impacted since it will use the default and if multiple variants exist, then either VerifyingKey needs to be duplicated or made generic or it will not be possible to conveniently deserialize with a specific validation variant, which is error-prone for anyone not using the default.
This PR is certainly a step in the right direction, but it'd be nice to address the problem on a higher level too.
How do you propose that could be done? Apart from adding FWIW: personally I think the default for Serde and other implementations should be RFC 8032. |
|
Feature flag will not work because it is additive and it is likely to have multiple variants enabled by different indirect dependencies. The most straightforward way is to rename The issue mentioned an enum, which could allow to retain As for serde, you can't change it without causing a lot of breakage in the ecosystem. If things compile, you generally expect it to work. Even major version bump will still be very frustrating IMHO due to how subtle the change is. And Since ZIP 215 happened first and basically all blockchains adopted this variant due to it being first (but not objectively better), changing the default would force effectively all current users to use non-default, at which point it might make sense to think about breaking API proper and do something better. Something like
So far I think enum with |
|
@tarcieri could you take another look at this PR, please? |
| bincode = "1" | ||
| criterion = { version = "0.5", features = ["html_reports"] } | ||
| hex = "0.4.2" | ||
| hex-literal = "0.4" |
There was a problem hiding this comment.
| hex-literal = "0.4" | |
| hex-literal = "1" |
| pub(super) fn check_modulus(repr: &CompressedEdwardsY) -> Choice { | ||
| // The modulus is: | ||
| // 0xecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f | ||
|
|
||
| // Remove the sign from the MSB. | ||
| let high_without_sign = repr.as_bytes()[31] & 0x7f; | ||
|
|
||
| // High-byte can never be bigger than 0x7f, point is always valid as | ||
| // long as its smaller. | ||
| let high = high_without_sign.ct_eq(&0x7f); | ||
| // If low-byte is bigger than 0xec and all other bytes are 0xff point | ||
| // is invalid. | ||
| let low = repr.as_bytes()[0].ct_gt(&0xec); | ||
| // If all other bytes are 0xff and the low-byte is bigger than 0xec | ||
| // point is invalid. | ||
| let mid = repr.as_bytes()[1..31].ct_eq(&[0xff; 30]); | ||
|
|
||
| !high | (!mid | !low) | ||
| } |
There was a problem hiding this comment.
Not really wild about this. Another way to check for a canonical representative is by decoding, re-encoding, and comparing equality.
As discussed in #626. I went ahead and adjusted
PrimeField::from_repr()to align with RustCrypto's implementations and provide a constant-time decompression method.I suggest to get rid of the enum entirely. Users who want to check for prime-order subgroup can do so via
is_torsion_free()to simplify the API. WDYT?Let me know if I can improve the documentation in any way.
Resolves #626.