Skip to content
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

Compressed #56

Merged
merged 12 commits into from
May 17, 2019
Merged

Compressed #56

merged 12 commits into from
May 17, 2019

Conversation

jannikluhn
Copy link
Contributor

What was wrong?

Missing support for compressed public keys (#55).

How was it fixed?

  • Add to/from_compressed_bytes methods to PublicKey class.
  • Add implementations for the coincurve and native backend.
  • Add some tests.

I'm not an expert in cryptography, so please review carefully, especially the native backend code.

Cute Animal Picture

Cute animal picture


def decompress_public_key_bytes(self,
compressed_public_key_bytes: bytes) -> bytes:
raise NotImplementedError()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self that we should convert these to be ABC,abstractmethod based sometime.

Copy link
Member

@pipermerriam pipermerriam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there is one more easy test that could be written to randomly generate a private key and validate that decompress(compress(original)) == original is always true (using hypothesis)

Also needs to update the documentation with these new APIs.

eth_keys/validation.py Outdated Show resolved Hide resolved
@jannikluhn
Copy link
Contributor Author

Added the suggested hypothesis test, improved the validation error message, and updated the readme.

Also, when looking at the readme I realized that the key compression API is duplicated: Once in PublicKey (where one would naturally look for it) and once in KeyAPI (where it doesn't make too much sense IMO). I therefore removed it from KeyAPI and udpated the tests accordingly.

Copy link
Collaborator

@carver carver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will wait a bit to see if Piper has more to add. Looks like one of the tests can be simplified.

prefix = b"\x02"
else:
prefix = b"\x03"
return prefix + pad32(int_to_big_endian(x))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol, padding the compressed key is a strange spec. I was wondering why the compressed key is always the same length...

tests/backends/test_native_backend_against_coincurve.py Outdated Show resolved Hide resolved
@carver
Copy link
Collaborator

carver commented May 14, 2019

Anything else to add today @pipermerriam ? If not, I'll merge by EOD.

Copy link
Member

@pipermerriam pipermerriam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good.

The hypothesis tests are probably only running a small number of runs IIRC so you might do a couple of local runs or repeat the hypothesis test runs on a loop for a while to stress test them a bit and see if you find any edge cases that demonstrate the two implementations diverging before you click merge.

uncompressed_public_key_bytes: bytes) -> bytes:
point = (
big_endian_to_int(uncompressed_public_key_bytes[:32]),
big_endian_to_int(uncompressed_public_key_bytes[32:]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows for an arbitrarily long second value were the provided uncompressed_public_key_bytes to be longer than 64 bytes. Am I missing something? Seems like this would cause weird/hard-to-debug downstream errors in some cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not part of the public API, all inputs should pass through datatypes.PublicKey. I still added validation now, just to be safe.

if prefix not in (2, 3):
raise ValueError("Invalid compressed public key")

x = big_endian_to_int(compressed_public_key_bytes[1:])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been on a big memoryview kick recently so feel free to ignore me. This would be slightly faster using a memoryview to prevent the copy. This is only safe if the thing you're passing it to doesn't try to mutate it which in this case I believe that condition is satisfied.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick benchmark and didn't see any improvement. I think the reason is that it would save one copy when slicing, but it adds one when creating the memoryview. But I will keep it in mind in the future (could be very useful for pyrlp/ssz).

@jannikluhn
Copy link
Contributor Author

I don't have write access here, can someone merge please?

@carver carver merged commit 0047363 into ethereum:master May 17, 2019
@carver
Copy link
Collaborator

carver commented May 17, 2019

Would you like a release after #58 is merged?

@jannikluhn
Copy link
Contributor Author

Thanks!

Would you like a release after #58 is merged?

Yep, that would be nice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants