diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2015-10-27 22:17:13 -0400 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2015-10-27 22:17:13 -0400 |
commit | d3f4ecdbefe20f4f9cb8b74701c5757557e476e1 (patch) | |
tree | 7bd4e100c3536859936c6453208f956719f1d7c5 | |
parent | cab8db3e9535a3900602931cb84d3f273f685d47 (diff) | |
parent | 197db3367ceecdbaf0adc5eaa9339b934b10acfe (diff) | |
download | cryptography-d3f4ecdbefe20f4f9cb8b74701c5757557e476e1.tar.gz cryptography-d3f4ecdbefe20f4f9cb8b74701c5757557e476e1.tar.bz2 cryptography-d3f4ecdbefe20f4f9cb8b74701c5757557e476e1.zip |
Merge pull request #2447 from reaperhulk/encode-decode-point
add support for encoding/decoding elliptic curve points
-rw-r--r-- | CHANGELOG.rst | 6 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/ec.rst | 32 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/ec.py | 25 | ||||
-rw-r--r-- | src/cryptography/utils.py | 7 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_ec.py | 67 |
5 files changed, 135 insertions, 2 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 08ac1093..26892736 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,12 @@ Changelog :func:`~cryptography.hazmat.primitives.keywrap.aes_key_wrap` and :func:`~cryptography.hazmat.primitives.keywrap.aes_key_unwrap`. * Added an ``__hash__`` method to :class:`~cryptography.x509.Name`. +* Add support for encoding and decoding elliptic curve points to a byte string + form using + :meth:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicNumbers.encode_point` + and + :meth:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicNumbers.from_encoded_point`. + 1.0.2 - 2015-09-27 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index 90e73711..c1619dd0 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -122,6 +122,37 @@ Elliptic Curve Signature Algorithms :returns: A new instance of a :class:`EllipticCurvePublicKey` provider. + .. method:: encode_point() + + .. versionadded:: 1.1 + + Encodes an elliptic curve point to a byte string as described in + `SEC 1 v2.0`_ section 2.3.3. This method only supports uncompressed + points. + + :return bytes: The encoded point. + + .. classmethod:: from_encoded_point(curve, data) + + .. versionadded:: 1.1 + + Decodes a byte string as described in `SEC 1 v2.0`_ section 2.3.3 and + returns an :class:`EllipticCurvePublicNumbers`. This method only + supports uncompressed points. + + :param curve: An + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve` + instance. + + :param bytes data: The serialized point byte string. + + :returns: An :class:`EllipticCurvePublicNumbers` instance. + + :raises ValueError: Raised on invalid point type or data length. + + :raises TypeError: Raised when curve is not an + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve`. + Elliptic Curve Key Exchange algorithm ------------------------------------- @@ -478,3 +509,4 @@ Key Interfaces .. _`ECDSA`: https://en.wikipedia.org/wiki/ECDSA .. _`EdDSA`: https://en.wikipedia.org/wiki/EdDSA .. _`forward secrecy`: https://en.wikipedia.org/wiki/Forward_secrecy +.. _`SEC 1 v2.0`: http://www.secg.org/sec1-v2.pdf diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py index c6f83667..7782133c 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -259,6 +259,31 @@ class EllipticCurvePublicNumbers(object): def public_key(self, backend): return backend.load_elliptic_curve_public_numbers(self) + def encode_point(self): + # key_size is in bits. Convert to bytes and round up + byte_length = (self.curve.key_size + 7) // 8 + return ( + b'\x04' + utils.int_to_bytes(self.x, byte_length) + + utils.int_to_bytes(self.y, byte_length) + ) + + @classmethod + def from_encoded_point(cls, curve, data): + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must be an EllipticCurve instance") + + if data.startswith(b'\x04'): + # key_size is in bits. Convert to bytes and round up + byte_length = (curve.key_size + 7) // 8 + if len(data) == 2 * byte_length + 1: + x = utils.int_from_bytes(data[1:byte_length + 1], 'big') + y = utils.int_from_bytes(data[byte_length + 1:], 'big') + return cls(x, y, curve) + else: + raise ValueError('Invalid elliptic curve point data length') + else: + raise ValueError('Unsupported elliptic curve point type') + curve = utils.read_only_property("_curve") x = utils.read_only_property("_x") y = utils.read_only_property("_y") diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index dac4046d..dbd961f7 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -48,9 +48,12 @@ else: return result -def int_to_bytes(integer): +def int_to_bytes(integer, length=None): hex_string = '%x' % integer - n = len(hex_string) + if length is None: + n = len(hex_string) + else: + n = length * 2 return binascii.unhexlify(hex_string.zfill(n + (n & 1))) diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index 4c4d5b90..5baaa3cd 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function +import binascii import itertools import os @@ -148,6 +149,72 @@ def test_ec_numbers(): ) +def test_encode_point(): + # secp256r1 point + x = int( + '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', + 16 + ) + y = int( + '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', + 16 + ) + pn = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()) + data = pn.encode_point() + assert data == binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae" + "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" + ) + + +def test_from_encoded_point(): + # secp256r1 point + data = binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae" + "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" + ) + pn = ec.EllipticCurvePublicNumbers.from_encoded_point( + ec.SECP256R1(), data + ) + assert pn.x == int( + '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', + 16 + ) + assert pn.y == int( + '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', + 16 + ) + + +def test_from_encoded_point_invalid_length(): + bad_data = binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae" + "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460" + ) + with pytest.raises(ValueError): + ec.EllipticCurvePublicNumbers.from_encoded_point( + ec.SECP384R1(), bad_data + ) + + +def test_from_encoded_point_unsupported_point_type(): + # set to point type 2. + unsupported_type = binascii.unhexlify( + "02233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22a" + ) + with pytest.raises(ValueError): + ec.EllipticCurvePublicNumbers.from_encoded_point( + ec.SECP256R1(), unsupported_type + ) + + +def test_from_encoded_point_not_a_curve(): + with pytest.raises(TypeError): + ec.EllipticCurvePublicNumbers.from_encoded_point( + "notacurve", b"\x04data" + ) + + @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) class TestECWithNumbers(object): @pytest.mark.parametrize( |