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