RustCrypto / elliptic-curves

Collection of pure Rust elliptic curve implementations: NIST P-224, P-256, P-384, P-521, secp256k1, SM2
683 stars 191 forks source link

k256 Schnorr / BIP340 doesn't handle the y odd case #1070

Open randombit opened 3 months ago

randombit commented 3 months ago

For both signature generation and verification, k256 doesn't seem to handle the case where the y coordinate of the public key is odd. I guess it is implicitly assuming that the user handles this, but this doesn't match how BIP340 describes the operation.

Both issues can be worked around by checking in advance if an odd y would occur and inverting the private or public key before use.

tarcieri commented 3 months ago

It did in my original implementation. It must've regressed at some point: https://github.com/RustCrypto/elliptic-curves/pull/482/files#diff-7ee5e04f685838d30d3bb56a9c532ffc01948c79f1c7a7faecc989186d803ee1R94-R98

tarcieri commented 3 months ago

@randombit on the SigningKey side of things, this logic should be handling it, eagerly inverting SigningKey in this case: https://github.com/RustCrypto/elliptic-curves/blob/a1fabfb/k256/src/schnorr/signing.rs#L124-L130

Are you encountering a codepath where this isn't happening, or do you want a lazy inversion instead of an eager one?

There are definitely test vectors in our suite which break if I remove that logic.

However k256 seems to assume/require that the public key is a point with both x and y coordinates

VerifyingKey::from_bytes uses AffinePoint::decompact which operates only on the x coordinate.

I guess that should be modified to try decompressing both odd and even y and going with whatever solution is valid.

tarcieri commented 2 months ago

@randombit can you provide a reproduction of where you think it's being mishandled?

randombit commented 2 months ago

I think the main thing that caught me up here is that k256::schnorr::VerifyingKey::try_from(AffinePoint) doesn't work if y is odd, and I didn't notice from_bytes. So I flipped the y of my public key points to make things work for verification. Then I checked signature generation and did not see any flip of the secret key happening there, missing that it happened during the type conversion instead. But it seems (outside from k256::schnorr::VerifyingKey::try_from rejecting points with odd y, which still doesn't seem right to me) everything is being correctly handled. Sorry for the noise.

tarcieri commented 2 months ago

outside from k256::schnorr::VerifyingKey::try_from rejecting points with odd y, which still doesn't seem right to me

It could be changed to auto-invert