-
Notifications
You must be signed in to change notification settings - Fork 330
Support for SEC compressed keys #54
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,15 @@ | ||
| import binascii | ||
|
|
||
| from . import ecdsa | ||
| from . import ellipticcurve | ||
| from . import der | ||
| from . import rfc6979 | ||
| from .curves import NIST192p, find_curve | ||
| from .numbertheory import square_root_mod_prime | ||
| from .six import PY3, b | ||
| from .util import string_to_number, number_to_string, randrange | ||
| from .util import sigencode_string, sigdecode_string | ||
| from .util import oid_ecPublicKey, encoded_oid_ecPublicKey | ||
| from .six import PY3, b | ||
| from hashlib import sha1 | ||
|
|
||
|
|
||
|
|
@@ -78,6 +80,32 @@ def from_der(klass, string): | |
| assert point_str.startswith(b("\x00\x04")) | ||
| return klass.from_string(point_str[2:], curve) | ||
|
|
||
| @classmethod | ||
| def from_sec(klass, string, curve=NIST192p, hashfunc=sha1, | ||
| validate_point=True): | ||
| """Convert a public key in SEC binary format to a verifying key.""" | ||
| # based on code from https://github.com/richardkiss/pycoin | ||
| if string.startswith(b('\x04')): | ||
| # uncompressed | ||
| return klass.from_string(string[1:], curve, hashfunc, | ||
| validate_point) | ||
| elif string.startswith(b('\x02')) or string.startswith(b('\x03')): | ||
| # compressed | ||
| is_even = string.startswith(b('\x02')) | ||
| x = string_to_number(string[1:]) | ||
| order = curve.order | ||
| p = curve.curve.p() | ||
| alpha = (pow(x, 3, p) + (curve.curve.a() * x) + curve.curve.b()) % p | ||
| beta = square_root_mod_prime(alpha, p) | ||
| if is_even == bool(beta & 1): | ||
| y = p - beta | ||
| else: | ||
| y = beta | ||
| if validate_point: | ||
| assert ecdsa.point_is_valid(curve.generator, x, y) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assertions are optimised out when compiling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case, what this means is that the klass.from_public_point() call will return a RuntimeError("Generator point has x or y out of range.") from ecdsa.Public_key initialization. What change would you make here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if I just add a "pass" it will work just fine, right? (similar issue around line 50) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made a small test using: and when I run that with -O it prints "a" and "b" showing that it "pass"es, while a non-optimised run will throw an AssertionError after printing "a" (as expected in this example). So I actually think there is no need to change this PR, nor the current https://github.com/warner/python-ecdsa/blob/master/src/ecdsa/keys.py#L49 @tomato42 and @techguy613 can you confirm that there is no need to change the PR, or clarify why it should and how? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't change this PR as it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it does need to to be changed - as asserts are optimised out, the call to now, either this call is necessary - then it's a big issue as the inputs may be attacker controlled - or this call is not necessary and then the whole There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok so why not remove the whole if statement then and the issue is resolved? The point is checked to be within the curve at a deeper level and will still throw an exception as I pointed out a little earlier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tomato42: with your arguments that it needs to change, the current https://github.com/warner/python-ecdsa/blob/master/src/ecdsa/keys.py#L49 construct also needs a change. I will adjust this PR based on how that is (proposed to be) changed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mdxs: that line 49 need to actually be changed to not use |
||
| point = ellipticcurve.Point(curve.curve, x, y, order) | ||
| return klass.from_public_point(point, curve, hashfunc) | ||
|
|
||
| def to_string(self): | ||
| # VerifyingKey.from_string(vk.to_string()) == vk as long as the | ||
| # curves are the same: the curve itself is not included in the | ||
|
|
@@ -99,6 +127,20 @@ def to_der(self): | |
| self.curve.encoded_oid), | ||
| der.encode_bitstring(point_str)) | ||
|
|
||
| def to_sec(self, compressed=True): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, |
||
| """Convert verifying key to the SEC binary format (as used by OpenSSL).""" | ||
| # based on code from https://github.com/richardkiss/pycoin | ||
| order = self.pubkey.order | ||
| x_str = number_to_string(self.pubkey.point.x(), order) | ||
| if compressed: | ||
| if self.pubkey.point.y() & 1: | ||
| return b('\x03') + x_str | ||
| else: | ||
| return b('\x02') + x_str | ||
| else: | ||
| y_str = number_to_string(self.pubkey.point.y(), order) | ||
| return b('\x04') + x_str + y_str | ||
|
|
||
| def verify(self, signature, data, hashfunc=None, sigdecode=sigdecode_string): | ||
| hashfunc = hashfunc or self.default_hashfunc | ||
| digest = hashfunc(data).digest() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why this couldn't be folded into
from_string()method—autodetect the format and use the necessary decoding