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

Fix Overflow Error #11

Merged
merged 8 commits into from
Jan 14, 2020
Merged

Fix Overflow Error #11

merged 8 commits into from
Jan 14, 2020

Conversation

GoodiesHQ
Copy link
Contributor

@GoodiesHQ GoodiesHQ commented Jan 9, 2020

Hello,

Using the curve "secp521k1", I was running into seemingly random instances of an OverflowError when encoding/decoding the points. For troubleshooting, I went into ecpy/curves.py and modified the encode_point to print out the calculated size (self.size >> 3) and the ACTUAL size, in bits, of the X and Y coordinates of the point. I noticed that sometimes it was too small.

Reproduce:

>>> import random
>>> from ecpy.curves import Curve
>>>
>>> curve = Curve.get_curve("secp521r1")
>>> p = 7777777 * curve.generator
>>> curve.encode_point(p)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\Austin Archer\AppData\Roaming\Python\Python38\site-packages\ecpy\curves.py", line 360, in encode_point
    x = bytearray(P.x.to_bytes(size,'big'))
OverflowError: int too big to convert

What I discovered was that around 50% of the time, the value self.size >> 3 is insufficient by one byte. In order to remedy this, the proper size of the numbers should be calculated by using ceil(self.size / 8), or a slightly faster method such as size = self.size >> 3 + bool(self.size % 8) which will result in the proper bit sizes each time.

This is an issue because the bit size of secp521k1 is 521, and 521 >> 3 is equal to 65, but 66 is often required.

@GoodiesHQ
Copy link
Contributor Author

GoodiesHQ commented Jan 10, 2020

I actually got some surprising results from a few methods... picked the fastest.

Note: Pretty sure % 8 is implemented as & 7 within Python because I got no appreciable difference between the two, and % 8 is simply more readable.

>>> from timeit import timeit
>>> count = 10**7
>>> 
>>> # METHOD 1:
>>> timeit("ceil(521 / 8)", setup="from math import ceil", number=count)
2.355680199999995

>>> # METHOD 2
>>> timeit("(521 >> 3) + bool(521 % 8)", number=count)
2.0705702999999858

>>> # METHOD 3
>>> timeit("(521 >> 3) + (1 if 521 % 8 else 0)", number=count)
0.6898246000000086

Found an even more performant method through testing.
@cslashm
Copy link
Owner

cslashm commented Jan 10, 2020

Thanks for reporting, Indeed 521 bit leads to 65.125 bytes :)

I think (self.size+7)>>3 will do the job in the fastest way. Can you update your PR if I'm right?

@GoodiesHQ
Copy link
Contributor Author

GoodiesHQ commented Jan 10, 2020

Thanks for the response! These two methods show little to no difference between execution speeds:

>>> from timeit import timeit
>>> count = 10**7
>>> timeit("(521 >> 3) + (not not 521 % 8)", number=count)
0.13011330000063026
>>> timeit("((521+7) >> 3)", number=count)
0.11733189999995375
>>> timeit("(521+7 >> 3)", number=count)
0.10996770000019751

however will say that your method is a bit more readable so I will go ahead and add that to the PR!

For fun:

>>> from dis import dis
>>> dis("x >> 3")
  1           0 LOAD_NAME                0 (x)
              2 LOAD_CONST               0 (3)
              4 BINARY_RSHIFT
              6 RETURN_VALUE
>>> dis("x >> 3 + (not not x & 7)")
  1           0 LOAD_NAME                0 (x)
              2 LOAD_CONST               0 (3)
              4 LOAD_NAME                0 (x)
              6 LOAD_CONST               1 (7)
              8 BINARY_AND
             10 UNARY_NOT
             12 UNARY_NOT
             14 BINARY_ADD
             16 BINARY_RSHIFT
             18 RETURN_VALUE
>>> dis("x+7 >> 3")
  1           0 LOAD_NAME                0 (x)
              2 LOAD_CONST               0 (7)
              4 BINARY_ADD
              6 LOAD_CONST               1 (3)
              8 BINARY_RSHIFT
             10 RETURN_VALUE

Optimization
@cslashm cslashm merged commit e38fa63 into cslashm:master Jan 14, 2020
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

2 participants