From 467072f7d50778f064f192b4e318c19c6cf98293 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 25 Oct 2015 15:44:29 -0500 Subject: add support for encoding/decoding elliptic curve points Based on the work of @ronf in #2346. --- docs/hazmat/primitives/asymmetric/utils.rst | 41 ++++++++++++ .../hazmat/primitives/asymmetric/utils.py | 34 ++++++++++ src/cryptography/utils.py | 7 ++- tests/hazmat/primitives/test_asym_utils.py | 73 +++++++++++++++++++++- 4 files changed, 151 insertions(+), 4 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index 07883598..825fe3c1 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -28,3 +28,44 @@ Asymmetric Utilities :param int s: The raw signature value ``s``. :return bytes: The encoded signature. + +.. function:: encode_ec_point(curve, x, y) + + .. versionadded:: 1.1 + + Encodes an elliptic curve point to a byte string as described in + _`SEC 1 v2.0` section 2.3.3. This function only supports uncompressed + points. + + :param curve: A :class:`EllipticCurve` provider. + + :param x: The x value of the point. + + :type: int or None + + :param int y: The y value of the point. + + :return bytes: The encoded point. + + :raises TypeError: Raised when curve is not an :class:`EllipticCurve`. + +.. function:: decode_ec_point(key_length, data) + + .. versionadded:: 1.1 + + Decodes a byte string as described in _`SEC 1 v2.0` section 2.3.3 to the + ``x`` and ``y`` integer values. This function only supports uncompressed + points. + + :param curve: A :class:`EllipticCurve` provider. + + :param bytes data: The serialized point byte string. + + :returns: The decoded tuple ``(x, y)``. + + :raises ValueError: Raised on invalid point type or data length. + + :raises TypeError: Raised when curve is not an :class:`EllipticCurve`. + + +.. _`SEC 1 v2.0`: http://www.secg.org/sec1-v2.pdf diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py index bad9ab73..b62eadf0 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/utils.py +++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py @@ -13,6 +13,7 @@ from pyasn1.type import namedtype, univ import six from cryptography import utils +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve class _DSSSigValue(univ.Sequence): @@ -71,3 +72,36 @@ def encode_dss_signature(r, s): sig.setComponentByName('r', r) sig.setComponentByName('s', s) return encoder.encode(sig) + + +def encode_ec_point(curve, x, y): + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must be an EllipticCurve instance") + + if x is None: + return b'\x00' + else: + # Get the ceiling of curve.key_size / 8 + byte_length = (curve.key_size + 7) // 8 + return ( + b'\x04' + utils.int_to_bytes(x, byte_length) + + utils.int_to_bytes(y, byte_length) + ) + + +def decode_ec_point(curve, data): + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must be an EllipticCurve instance") + + if data == b'\x00': + return None, None + elif data.startswith(b'\x04'): + # Get the ceiling of curve.key_size / 8 + byte_length = (curve.key_size + 7) // 8 + if len(data) == 2 * byte_length + 1: + return (utils.int_from_bytes(data[1:byte_length + 1], 'big'), + utils.int_from_bytes(data[byte_length + 1:], 'big')) + else: + raise ValueError('Invalid elliptic curve point data length') + else: + raise ValueError('Unsupported elliptic curve point type') 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_asym_utils.py b/tests/hazmat/primitives/test_asym_utils.py index b9971137..c713e9c6 100644 --- a/tests/hazmat/primitives/test_asym_utils.py +++ b/tests/hazmat/primitives/test_asym_utils.py @@ -4,11 +4,14 @@ from __future__ import absolute_import, division, print_function +import binascii + import pytest +from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import ( - decode_dss_signature, decode_rfc6979_signature, - encode_dss_signature, encode_rfc6979_signature + decode_dss_signature, decode_ec_point, decode_rfc6979_signature, + encode_dss_signature, encode_ec_point, encode_rfc6979_signature ) @@ -76,3 +79,69 @@ def test_decode_dss_invalid_asn1(): # This is the BER "end-of-contents octets," which older versions of # pyasn1 are wrongly willing to return from top-level DER decoding. decode_dss_signature(b"\x00\x00") + + +def test_encode_ec_point_none(): + assert encode_ec_point(ec.SECP384R1(), None, 100) == b"\x00" + + +def test_encode_wrong_curve_type(): + with pytest.raises(TypeError): + encode_ec_point("notacurve", 3, 4) + + +def test_encode_ec_point(): + # secp256r1 point + x = int( + '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', 16 + ) + y = int( + '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', 16 + ) + data = encode_ec_point(ec.SECP256R1(), x, y) + assert data == binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3ea" + "2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" + ) + + +def test_decode_ec_point_none(): + assert decode_ec_point(ec.SECP384R1(), b"\x00") == (None, None) + + +def test_decode_ec_point(): + # secp256r1 point + data = binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3ea" + "2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" + ) + x, y = decode_ec_point(ec.SECP256R1(), data) + assert x == int( + '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', 16 + ) + assert y == int( + '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', 16 + ) + + +def test_decode_ec_point_invalid_length(): + bad_data = binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3ea" + "2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460" + ) + with pytest.raises(ValueError): + decode_ec_point(ec.SECP384R1(), bad_data) + + +def test_decode_ec_point_unsupported_point_type(): + # set to point type 2. + unsupported_type = binascii.unhexlify( + "02233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3e" + ) + with pytest.raises(ValueError): + decode_ec_point(ec.SECP256R1(), unsupported_type) + + +def test_decode_wrong_curve_type(): + with pytest.raises(TypeError): + decode_ec_point("notacurve", b"\x02data") -- cgit v1.2.3 From d9cfab3fe2d9772d4f5812377a89d40624d20673 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 09:02:36 +0900 Subject: fix some docs --- docs/hazmat/primitives/asymmetric/utils.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index 825fe3c1..e67dd734 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -34,10 +34,12 @@ Asymmetric Utilities .. versionadded:: 1.1 Encodes an elliptic curve point to a byte string as described in - _`SEC 1 v2.0` section 2.3.3. This function only supports uncompressed + `SEC 1 v2.0`_ section 2.3.3. This function only supports uncompressed points. - :param curve: A :class:`EllipticCurve` provider. + :param curve: A + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve` + provider. :param x: The x value of the point. @@ -47,17 +49,20 @@ Asymmetric Utilities :return bytes: The encoded point. - :raises TypeError: Raised when curve is not an :class:`EllipticCurve`. + :raises TypeError: Raised when curve is not an + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve`. .. function:: decode_ec_point(key_length, data) .. versionadded:: 1.1 - Decodes a byte string as described in _`SEC 1 v2.0` section 2.3.3 to the + Decodes a byte string as described in `SEC 1 v2.0`_ section 2.3.3 to the ``x`` and ``y`` integer values. This function only supports uncompressed points. - :param curve: A :class:`EllipticCurve` provider. + :param curve: A + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve` + provider. :param bytes data: The serialized point byte string. @@ -65,7 +70,8 @@ Asymmetric Utilities :raises ValueError: Raised on invalid point type or data length. - :raises TypeError: Raised when curve is not an :class:`EllipticCurve`. + :raises TypeError: Raised when curve is not an + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve`. .. _`SEC 1 v2.0`: http://www.secg.org/sec1-v2.pdf -- cgit v1.2.3 From 3568563e0ecdec07606c8b5f3fed6eaea1fa95fd Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 10:20:45 +0900 Subject: rst continues to be a mystery --- docs/hazmat/primitives/asymmetric/utils.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index e67dd734..829edb99 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -42,8 +42,7 @@ Asymmetric Utilities provider. :param x: The x value of the point. - - :type: int or None + :type x: int or `None` :param int y: The y value of the point. -- cgit v1.2.3 From e4150bcc07fb2c68e3c860c31766b4282bba8740 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 12:29:58 +0900 Subject: remove support for null points, improve docs --- docs/hazmat/primitives/asymmetric/utils.rst | 11 +++++------ src/cryptography/hazmat/primitives/asymmetric/utils.py | 8 ++++---- tests/hazmat/primitives/test_asym_utils.py | 6 ++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index 829edb99..79d14dae 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -37,12 +37,11 @@ Asymmetric Utilities `SEC 1 v2.0`_ section 2.3.3. This function only supports uncompressed points. - :param curve: A + :param curve: An :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve` - provider. + instance. - :param x: The x value of the point. - :type x: int or `None` + :param int x: The x value of the point. :param int y: The y value of the point. @@ -59,9 +58,9 @@ Asymmetric Utilities ``x`` and ``y`` integer values. This function only supports uncompressed points. - :param curve: A + :param curve: An :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve` - provider. + instance. :param bytes data: The serialized point byte string. diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py index b62eadf0..57dea41a 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/utils.py +++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py @@ -79,9 +79,9 @@ def encode_ec_point(curve, x, y): raise TypeError("curve must be an EllipticCurve instance") if x is None: - return b'\x00' + raise ValueError("null points are not supported") else: - # Get the ceiling of curve.key_size / 8 + # key_size is in bits. Convert to bytes and round up byte_length = (curve.key_size + 7) // 8 return ( b'\x04' + utils.int_to_bytes(x, byte_length) + @@ -94,9 +94,9 @@ def decode_ec_point(curve, data): raise TypeError("curve must be an EllipticCurve instance") if data == b'\x00': - return None, None + raise ValueError("null points are not supported") elif data.startswith(b'\x04'): - # Get the ceiling of curve.key_size / 8 + # 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: return (utils.int_from_bytes(data[1:byte_length + 1], 'big'), diff --git a/tests/hazmat/primitives/test_asym_utils.py b/tests/hazmat/primitives/test_asym_utils.py index c713e9c6..22551bc4 100644 --- a/tests/hazmat/primitives/test_asym_utils.py +++ b/tests/hazmat/primitives/test_asym_utils.py @@ -82,7 +82,8 @@ def test_decode_dss_invalid_asn1(): def test_encode_ec_point_none(): - assert encode_ec_point(ec.SECP384R1(), None, 100) == b"\x00" + with pytest.raises(ValueError): + encode_ec_point(ec.SECP384R1(), None, 100) def test_encode_wrong_curve_type(): @@ -106,7 +107,8 @@ def test_encode_ec_point(): def test_decode_ec_point_none(): - assert decode_ec_point(ec.SECP384R1(), b"\x00") == (None, None) + with pytest.raises(ValueError): + decode_ec_point(ec.SECP384R1(), b"\x00") def test_decode_ec_point(): -- cgit v1.2.3 From 13d4e74b13832b495ddfdff043376144d8ada66a Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 16:47:55 +0900 Subject: modify approach to use EllipticCurvePublicNumbers methods --- docs/hazmat/primitives/asymmetric/ec.rst | 32 +++++++++ docs/hazmat/primitives/asymmetric/utils.rst | 45 ------------- .../hazmat/primitives/asymmetric/ec.py | 28 ++++++++ .../hazmat/primitives/asymmetric/utils.py | 34 ---------- tests/hazmat/primitives/test_asym_utils.py | 75 +--------------------- tests/hazmat/primitives/test_ec.py | 74 +++++++++++++++++++++ 6 files changed, 136 insertions(+), 152 deletions(-) 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/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index 79d14dae..07883598 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -28,48 +28,3 @@ Asymmetric Utilities :param int s: The raw signature value ``s``. :return bytes: The encoded signature. - -.. function:: encode_ec_point(curve, x, y) - - .. versionadded:: 1.1 - - Encodes an elliptic curve point to a byte string as described in - `SEC 1 v2.0`_ section 2.3.3. This function only supports uncompressed - points. - - :param curve: An - :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve` - instance. - - :param int x: The x value of the point. - - :param int y: The y value of the point. - - :return bytes: The encoded point. - - :raises TypeError: Raised when curve is not an - :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve`. - -.. function:: decode_ec_point(key_length, data) - - .. versionadded:: 1.1 - - Decodes a byte string as described in `SEC 1 v2.0`_ section 2.3.3 to the - ``x`` and ``y`` integer values. This function only supports uncompressed - points. - - :param curve: An - :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve` - instance. - - :param bytes data: The serialized point byte string. - - :returns: The decoded tuple ``(x, y)``. - - :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`. - - -.. _`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..f25ea6de 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -259,6 +259,34 @@ 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 == b'\x00': + raise ValueError("null points are not supported") + elif 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') + else: + raise ValueError('Invalid elliptic curve point data length') + else: + raise ValueError('Unsupported elliptic curve point type') + + return cls(x, y, curve) + curve = utils.read_only_property("_curve") x = utils.read_only_property("_x") y = utils.read_only_property("_y") diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py index 57dea41a..bad9ab73 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/utils.py +++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py @@ -13,7 +13,6 @@ from pyasn1.type import namedtype, univ import six from cryptography import utils -from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve class _DSSSigValue(univ.Sequence): @@ -72,36 +71,3 @@ def encode_dss_signature(r, s): sig.setComponentByName('r', r) sig.setComponentByName('s', s) return encoder.encode(sig) - - -def encode_ec_point(curve, x, y): - if not isinstance(curve, EllipticCurve): - raise TypeError("curve must be an EllipticCurve instance") - - if x is None: - raise ValueError("null points are not supported") - else: - # key_size is in bits. Convert to bytes and round up - byte_length = (curve.key_size + 7) // 8 - return ( - b'\x04' + utils.int_to_bytes(x, byte_length) + - utils.int_to_bytes(y, byte_length) - ) - - -def decode_ec_point(curve, data): - if not isinstance(curve, EllipticCurve): - raise TypeError("curve must be an EllipticCurve instance") - - if data == b'\x00': - raise ValueError("null points are not supported") - elif 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: - return (utils.int_from_bytes(data[1:byte_length + 1], 'big'), - utils.int_from_bytes(data[byte_length + 1:], 'big')) - else: - raise ValueError('Invalid elliptic curve point data length') - else: - raise ValueError('Unsupported elliptic curve point type') diff --git a/tests/hazmat/primitives/test_asym_utils.py b/tests/hazmat/primitives/test_asym_utils.py index 22551bc4..b9971137 100644 --- a/tests/hazmat/primitives/test_asym_utils.py +++ b/tests/hazmat/primitives/test_asym_utils.py @@ -4,14 +4,11 @@ from __future__ import absolute_import, division, print_function -import binascii - import pytest -from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import ( - decode_dss_signature, decode_ec_point, decode_rfc6979_signature, - encode_dss_signature, encode_ec_point, encode_rfc6979_signature + decode_dss_signature, decode_rfc6979_signature, + encode_dss_signature, encode_rfc6979_signature ) @@ -79,71 +76,3 @@ def test_decode_dss_invalid_asn1(): # This is the BER "end-of-contents octets," which older versions of # pyasn1 are wrongly willing to return from top-level DER decoding. decode_dss_signature(b"\x00\x00") - - -def test_encode_ec_point_none(): - with pytest.raises(ValueError): - encode_ec_point(ec.SECP384R1(), None, 100) - - -def test_encode_wrong_curve_type(): - with pytest.raises(TypeError): - encode_ec_point("notacurve", 3, 4) - - -def test_encode_ec_point(): - # secp256r1 point - x = int( - '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', 16 - ) - y = int( - '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', 16 - ) - data = encode_ec_point(ec.SECP256R1(), x, y) - assert data == binascii.unhexlify( - "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3ea" - "2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" - ) - - -def test_decode_ec_point_none(): - with pytest.raises(ValueError): - decode_ec_point(ec.SECP384R1(), b"\x00") - - -def test_decode_ec_point(): - # secp256r1 point - data = binascii.unhexlify( - "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3ea" - "2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" - ) - x, y = decode_ec_point(ec.SECP256R1(), data) - assert x == int( - '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', 16 - ) - assert y == int( - '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', 16 - ) - - -def test_decode_ec_point_invalid_length(): - bad_data = binascii.unhexlify( - "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3ea" - "2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460" - ) - with pytest.raises(ValueError): - decode_ec_point(ec.SECP384R1(), bad_data) - - -def test_decode_ec_point_unsupported_point_type(): - # set to point type 2. - unsupported_type = binascii.unhexlify( - "02233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec3e" - ) - with pytest.raises(ValueError): - decode_ec_point(ec.SECP256R1(), unsupported_type) - - -def test_decode_wrong_curve_type(): - with pytest.raises(TypeError): - decode_ec_point("notacurve", b"\x02data") diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index 4c4d5b90..ac1ba27a 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,79 @@ 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_null(): + with pytest.raises(ValueError): + ec.EllipticCurvePublicNumbers.from_encoded_point( + ec.SECP384R1(), b"\x00" + ) + + +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( -- cgit v1.2.3 From 1a1b1157a2d393b314f3e1303f2f724118d2cae2 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 28 Oct 2015 09:33:05 +0900 Subject: add changelog entry --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) 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 ~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From 197db3367ceecdbaf0adc5eaa9339b934b10acfe Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 28 Oct 2015 10:09:49 +0900 Subject: address review feedback --- src/cryptography/hazmat/primitives/asymmetric/ec.py | 7 ++----- tests/hazmat/primitives/test_ec.py | 7 ------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py index f25ea6de..7782133c 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -272,21 +272,18 @@ class EllipticCurvePublicNumbers(object): if not isinstance(curve, EllipticCurve): raise TypeError("curve must be an EllipticCurve instance") - if data == b'\x00': - raise ValueError("null points are not supported") - elif data.startswith(b'\x04'): + 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') - return cls(x, y, curve) - curve = utils.read_only_property("_curve") x = utils.read_only_property("_x") y = utils.read_only_property("_y") diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index ac1ba27a..5baaa3cd 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -167,13 +167,6 @@ def test_encode_point(): ) -def test_from_encoded_point_null(): - with pytest.raises(ValueError): - ec.EllipticCurvePublicNumbers.from_encoded_point( - ec.SECP384R1(), b"\x00" - ) - - def test_from_encoded_point(): # secp256r1 point data = binascii.unhexlify( -- cgit v1.2.3