diff options
-rw-r--r-- | CHANGELOG.rst | 6 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/dsa.rst | 8 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/ec.rst | 8 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/utils.rst | 12 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/utils.py | 26 | ||||
-rw-r--r-- | src/cryptography/utils.py | 1 | ||||
-rw-r--r-- | src/cryptography/x509/__init__.py | 20 | ||||
-rw-r--r-- | src/cryptography/x509/base.py | 146 | ||||
-rw-r--r-- | src/cryptography/x509/extensions.py | 145 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_asym_utils.py | 50 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_dsa.py | 4 |
11 files changed, 233 insertions, 193 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5315b6ea..21319f8b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -54,6 +54,12 @@ Changelog * Add support for creating certificate signing requests with :class:`~cryptography.x509.CertificateSigningRequestBuilder`. This includes support for the same extensions supported in the ``CertificateBuilder``. +* Deprecate ``encode_rfc6979_signature`` and ``decode_rfc6979_signature`` in + favor of + :func:`~cryptography.hazmat.primitives.asymmetric.utils.encode_dss_signature` + and + :func:`~cryptography.hazmat.primitives.asymmetric.utils.decode_dss_signature`. + 0.9.3 - 2015-07-09 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst index 2ceb7d7d..4eb17e30 100644 --- a/docs/hazmat/primitives/asymmetric/dsa.rst +++ b/docs/hazmat/primitives/asymmetric/dsa.rst @@ -80,8 +80,8 @@ provider. >>> signature = signer.finalize() The ``signature`` is a ``bytes`` object, whose contents is DER encoded as -described in :rfc:`6979`. This can be decoded using -:func:`~cryptography.hazmat.primitives.asymmetric.utils.decode_rfc6979_signature`. +described in :rfc:`3279`. This can be decoded using +:func:`~cryptography.hazmat.primitives.asymmetric.utils.decode_dss_signature`. Verification ~~~~~~~~~~~~ @@ -264,7 +264,7 @@ Key interfaces Sign data which can be verified later by others using the public key. The signature is formatted as DER-encoded bytes, as specified in - :rfc:`6979`. + :rfc:`3279`. :param algorithm: An instance of a :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` @@ -354,7 +354,7 @@ Key interfaces key. :param bytes signature: The signature to verify. DER encoded as - specified in :rfc:`6979`. + specified in :rfc:`3279`. :param algorithm: An instance of a :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index e0abe0ab..323f4c3f 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -49,8 +49,8 @@ Elliptic Curve Signature Algorithms >>> signature = signer.finalize() The ``signature`` is a ``bytes`` object, whose contents is DER encoded as - described in :rfc:`6979`. This can be decoded using - :func:`~cryptography.hazmat.primitives.asymmetric.utils.decode_rfc6979_signature`. + described in :rfc:`3279`. This can be decoded using + :func:`~cryptography.hazmat.primitives.asymmetric.utils.decode_dss_signature`. @@ -306,7 +306,7 @@ Key Interfaces Sign data which can be verified later by others using the public key. The signature is formatted as DER-encoded bytes, as specified in - :rfc:`6979`. + :rfc:`3279`. :param signature_algorithm: An instance of a :class:`EllipticCurveSignatureAlgorithm` provider. @@ -372,7 +372,7 @@ Key Interfaces key. :param bytes signature: The signature to verify. DER encoded as - specified in :rfc:`6979`. + specified in :rfc:`3279`. :param signature_algorithm: An instance of a :class:`EllipticCurveSignatureAlgorithm` provider. diff --git a/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index 7380f0b5..07883598 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -6,10 +6,11 @@ Asymmetric Utilities .. currentmodule:: cryptography.hazmat.primitives.asymmetric.utils -.. function:: decode_rfc6979_signature(signature) +.. function:: decode_dss_signature(signature) - Takes in :rfc:`6979` signatures generated by the DSA/ECDSA signers and - returns a tuple ``(r, s)``. + Takes in signatures generated by the DSA/ECDSA signers and returns a + tuple ``(r, s)``. These signatures are ASN.1 encoded ``Dss-Sig-Value`` + sequences (as defined in :rfc:`3279`) :param bytes signature: The signature to decode. @@ -17,9 +18,10 @@ Asymmetric Utilities :raises ValueError: Raised if the signature is malformed. -.. function:: encode_rfc6979_signature(r, s) +.. function:: encode_dss_signature(r, s) - Creates an :rfc:`6979` byte string from raw signature values. + Creates an ASN.1 encoded ``Dss-Sig-Value`` (as defined in :rfc:`3279`) from + raw ``r`` and ``s`` values. :param int r: The raw signature value ``r``. diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py index a03025bb..bad9ab73 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/utils.py +++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py @@ -4,12 +4,16 @@ from __future__ import absolute_import, division, print_function +import warnings + from pyasn1.codec.der import decoder, encoder from pyasn1.error import PyAsn1Error from pyasn1.type import namedtype, univ import six +from cryptography import utils + class _DSSSigValue(univ.Sequence): componentType = namedtype.NamedTypes( @@ -19,6 +23,17 @@ class _DSSSigValue(univ.Sequence): def decode_rfc6979_signature(signature): + warnings.warn( + "decode_rfc6979_signature is deprecated and will " + "be removed in a future version, use decode_dss_signature instead " + "instead.", + utils.DeprecatedIn10, + stacklevel=2 + ) + return decode_dss_signature(signature) + + +def decode_dss_signature(signature): try: data, remaining = decoder.decode(signature, asn1Spec=_DSSSigValue()) except PyAsn1Error: @@ -35,6 +50,17 @@ def decode_rfc6979_signature(signature): def encode_rfc6979_signature(r, s): + warnings.warn( + "encode_rfc6979_signature is deprecated and will " + "be removed in a future version, use encode_dss_signature instead " + "instead.", + utils.DeprecatedIn10, + stacklevel=2 + ) + return encode_dss_signature(r, s) + + +def encode_dss_signature(r, s): if ( not isinstance(r, six.integer_types) or not isinstance(s, six.integer_types) diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index 993571bd..237d5968 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -13,6 +13,7 @@ import warnings DeprecatedIn09 = DeprecationWarning +DeprecatedIn10 = PendingDeprecationWarning def read_only_property(name): diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index a6d376b5..1aa2598b 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -7,19 +7,19 @@ from __future__ import absolute_import, division, print_function from cryptography.x509.base import ( Certificate, CertificateBuilder, CertificateRevocationList, CertificateSigningRequest, CertificateSigningRequestBuilder, - Extension, ExtensionType, GeneralNames, - InvalidVersion, IssuerAlternativeName, - ObjectIdentifier, RevokedCertificate, SubjectAlternativeName, - Version, load_der_x509_certificate, - load_der_x509_csr, load_pem_x509_certificate, load_pem_x509_csr, + InvalidVersion, RevokedCertificate, + Version, load_der_x509_certificate, load_der_x509_csr, + load_pem_x509_certificate, load_pem_x509_csr, ) from cryptography.x509.extensions import ( AccessDescription, AuthorityInformationAccess, AuthorityKeyIdentifier, BasicConstraints, CRLDistributionPoints, CertificatePolicies, DistributionPoint, DuplicateExtension, - ExtendedKeyUsage, ExtensionNotFound, Extensions, InhibitAnyPolicy, - KeyUsage, NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation, - ReasonFlags, SubjectKeyIdentifier, UnsupportedExtension, UserNotice + ExtendedKeyUsage, Extension, ExtensionNotFound, ExtensionType, Extensions, + GeneralNames, InhibitAnyPolicy, IssuerAlternativeName, KeyUsage, + NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation, + ReasonFlags, SubjectAlternativeName, SubjectKeyIdentifier, + UnsupportedExtension, UserNotice ) from cryptography.x509.general_name import ( DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name, @@ -29,8 +29,8 @@ from cryptography.x509.general_name import ( from cryptography.x509.name import Name, NameAttribute from cryptography.x509.oid import ( AuthorityInformationAccessOID, CRLExtensionOID, CertificatePoliciesOID, - ExtendedKeyUsageOID, ExtensionOID, NameOID, SignatureAlgorithmOID, - _SIG_OIDS_TO_HASH + ExtendedKeyUsageOID, ExtensionOID, NameOID, ObjectIdentifier, + SignatureAlgorithmOID, _SIG_OIDS_TO_HASH ) diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 312eea0c..27eafac6 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -12,11 +12,8 @@ import six from cryptography import utils from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa -from cryptography.x509.general_name import GeneralName, OtherName +from cryptography.x509.extensions import Extension, ExtensionType from cryptography.x509.name import Name -from cryptography.x509.oid import ( - ExtensionOID, ObjectIdentifier -) _UNIX_EPOCH = datetime.datetime(1970, 1, 1) @@ -49,147 +46,6 @@ class InvalidVersion(Exception): self.parsed_version = parsed_version -class Extension(object): - def __init__(self, oid, critical, value): - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - - if not isinstance(critical, bool): - raise TypeError("critical must be a boolean value") - - self._oid = oid - self._critical = critical - self._value = value - - oid = utils.read_only_property("_oid") - critical = utils.read_only_property("_critical") - value = utils.read_only_property("_value") - - def __repr__(self): - return ("<Extension(oid={0.oid}, critical={0.critical}, " - "value={0.value})>").format(self) - - def __eq__(self, other): - if not isinstance(other, Extension): - return NotImplemented - - return ( - self.oid == other.oid and - self.critical == other.critical and - self.value == other.value - ) - - def __ne__(self, other): - return not self == other - - -@six.add_metaclass(abc.ABCMeta) -class ExtensionType(object): - @abc.abstractproperty - def oid(self): - """ - Returns the oid associated with the given extension type. - """ - - -class GeneralNames(object): - def __init__(self, general_names): - if not all(isinstance(x, GeneralName) for x in general_names): - raise TypeError( - "Every item in the general_names list must be an " - "object conforming to the GeneralName interface" - ) - - self._general_names = general_names - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - # Return the value of each GeneralName, except for OtherName instances - # which we return directly because it has two important properties not - # just one value. - objs = (i for i in self if isinstance(i, type)) - if type != OtherName: - objs = (i.value for i in objs) - return list(objs) - - def __repr__(self): - return "<GeneralNames({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, GeneralNames): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class SubjectAlternativeName(object): - oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME - - def __init__(self, general_names): - self._general_names = GeneralNames(general_names) - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - return self._general_names.get_values_for_type(type) - - def __repr__(self): - return "<SubjectAlternativeName({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, SubjectAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class IssuerAlternativeName(object): - oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME - - def __init__(self, general_names): - self._general_names = GeneralNames(general_names) - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - return self._general_names.get_values_for_type(type) - - def __repr__(self): - return "<IssuerAlternativeName({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, IssuerAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - @six.add_metaclass(abc.ABCMeta) class Certificate(object): @abc.abstractmethod diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index 92a37357..798a0e3a 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function +import abc import hashlib import ipaddress from enum import Enum @@ -15,8 +16,7 @@ import six from cryptography import utils from cryptography.hazmat.primitives import serialization -from cryptography.x509.base import ExtensionType -from cryptography.x509.general_name import GeneralName, IPAddress +from cryptography.x509.general_name import GeneralName, IPAddress, OtherName from cryptography.x509.name import Name from cryptography.x509.oid import ( AuthorityInformationAccessOID, ExtensionOID, ObjectIdentifier @@ -69,6 +69,15 @@ class ExtensionNotFound(Exception): self.oid = oid +@six.add_metaclass(abc.ABCMeta) +class ExtensionType(object): + @abc.abstractproperty + def oid(self): + """ + Returns the oid associated with the given extension type. + """ + + class Extensions(object): def __init__(self, extensions): self._extensions = extensions @@ -769,3 +778,135 @@ class NameConstraints(object): permitted_subtrees = utils.read_only_property("_permitted_subtrees") excluded_subtrees = utils.read_only_property("_excluded_subtrees") + + +class Extension(object): + def __init__(self, oid, critical, value): + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + if not isinstance(critical, bool): + raise TypeError("critical must be a boolean value") + + self._oid = oid + self._critical = critical + self._value = value + + oid = utils.read_only_property("_oid") + critical = utils.read_only_property("_critical") + value = utils.read_only_property("_value") + + def __repr__(self): + return ("<Extension(oid={0.oid}, critical={0.critical}, " + "value={0.value})>").format(self) + + def __eq__(self, other): + if not isinstance(other, Extension): + return NotImplemented + + return ( + self.oid == other.oid and + self.critical == other.critical and + self.value == other.value + ) + + def __ne__(self, other): + return not self == other + + +class GeneralNames(object): + def __init__(self, general_names): + if not all(isinstance(x, GeneralName) for x in general_names): + raise TypeError( + "Every item in the general_names list must be an " + "object conforming to the GeneralName interface" + ) + + self._general_names = general_names + + def __iter__(self): + return iter(self._general_names) + + def __len__(self): + return len(self._general_names) + + def get_values_for_type(self, type): + # Return the value of each GeneralName, except for OtherName instances + # which we return directly because it has two important properties not + # just one value. + objs = (i for i in self if isinstance(i, type)) + if type != OtherName: + objs = (i.value for i in objs) + return list(objs) + + def __repr__(self): + return "<GeneralNames({0})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, GeneralNames): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class SubjectAlternativeName(object): + oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME + + def __init__(self, general_names): + self._general_names = GeneralNames(general_names) + + def __iter__(self): + return iter(self._general_names) + + def __len__(self): + return len(self._general_names) + + def get_values_for_type(self, type): + return self._general_names.get_values_for_type(type) + + def __repr__(self): + return "<SubjectAlternativeName({0})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, SubjectAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class IssuerAlternativeName(object): + oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME + + def __init__(self, general_names): + self._general_names = GeneralNames(general_names) + + def __iter__(self): + return iter(self._general_names) + + def __len__(self): + return len(self._general_names) + + def get_values_for_type(self, type): + return self._general_names.get_values_for_type(type) + + def __repr__(self): + return "<IssuerAlternativeName({0})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, IssuerAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other diff --git a/tests/hazmat/primitives/test_asym_utils.py b/tests/hazmat/primitives/test_asym_utils.py index 35b77ca4..b9971137 100644 --- a/tests/hazmat/primitives/test_asym_utils.py +++ b/tests/hazmat/primitives/test_asym_utils.py @@ -7,64 +7,72 @@ from __future__ import absolute_import, division, print_function import pytest from cryptography.hazmat.primitives.asymmetric.utils import ( - decode_rfc6979_signature, encode_rfc6979_signature + decode_dss_signature, decode_rfc6979_signature, + encode_dss_signature, encode_rfc6979_signature ) -def test_rfc6979_signature(): - sig = encode_rfc6979_signature(1, 1) +def test_deprecated_rfc6979_signature(): + sig = pytest.deprecated_call(encode_rfc6979_signature, 1, 1) assert sig == b"0\x06\x02\x01\x01\x02\x01\x01" - assert decode_rfc6979_signature(sig) == (1, 1) + decoded = pytest.deprecated_call(decode_rfc6979_signature, sig) + assert decoded == (1, 1) + + +def test_dss_signature(): + sig = encode_dss_signature(1, 1) + assert sig == b"0\x06\x02\x01\x01\x02\x01\x01" + assert decode_dss_signature(sig) == (1, 1) r_s1 = ( 1037234182290683143945502320610861668562885151617, 559776156650501990899426031439030258256861634312 ) - sig2 = encode_rfc6979_signature(*r_s1) + sig2 = encode_dss_signature(*r_s1) assert sig2 == ( b'0-\x02\x15\x00\xb5\xaf0xg\xfb\x8bT9\x00\x13\xccg\x02\r\xdf\x1f,\x0b' b'\x81\x02\x14b\r;"\xabP1D\x0c>5\xea\xb6\xf4\x81)\x8f\x9e\x9f\x08' ) - assert decode_rfc6979_signature(sig2) == r_s1 + assert decode_dss_signature(sig2) == r_s1 - sig3 = encode_rfc6979_signature(0, 0) + sig3 = encode_dss_signature(0, 0) assert sig3 == b"0\x06\x02\x01\x00\x02\x01\x00" - assert decode_rfc6979_signature(sig3) == (0, 0) + assert decode_dss_signature(sig3) == (0, 0) - sig4 = encode_rfc6979_signature(-1, 0) + sig4 = encode_dss_signature(-1, 0) assert sig4 == b"0\x06\x02\x01\xFF\x02\x01\x00" - assert decode_rfc6979_signature(sig4) == (-1, 0) + assert decode_dss_signature(sig4) == (-1, 0) -def test_encode_rfc6979_non_integer(): +def test_encode_dss_non_integer(): with pytest.raises(ValueError): - encode_rfc6979_signature("h", 3) + encode_dss_signature("h", 3) with pytest.raises(ValueError): - encode_rfc6979_signature("3", "2") + encode_dss_signature("3", "2") with pytest.raises(ValueError): - encode_rfc6979_signature(3, "h") + encode_dss_signature(3, "h") with pytest.raises(ValueError): - encode_rfc6979_signature(3.3, 1.2) + encode_dss_signature(3.3, 1.2) with pytest.raises(ValueError): - encode_rfc6979_signature("hello", "world") + encode_dss_signature("hello", "world") -def test_decode_rfc6979_trailing_bytes(): +def test_decode_dss_trailing_bytes(): with pytest.raises(ValueError): - decode_rfc6979_signature(b"0\x06\x02\x01\x01\x02\x01\x01\x00\x00\x00") + decode_dss_signature(b"0\x06\x02\x01\x01\x02\x01\x01\x00\x00\x00") -def test_decode_rfc6979_invalid_asn1(): +def test_decode_dss_invalid_asn1(): with pytest.raises(ValueError): # This byte sequence has an invalid ASN.1 sequence length as well as # an invalid integer length for the second integer. - decode_rfc6979_signature(b"0\x07\x02\x01\x01\x02\x02\x01") + decode_dss_signature(b"0\x07\x02\x01\x01\x02\x02\x01") with pytest.raises(ValueError): # This is the BER "end-of-contents octets," which older versions of # pyasn1 are wrongly willing to return from top-level DER decoding. - decode_rfc6979_signature(b"\x00\x00") + decode_dss_signature(b"\x00\x00") diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index b6b0de94..c263ef2b 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -17,7 +17,7 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa from cryptography.hazmat.primitives.asymmetric.utils import ( - encode_rfc6979_signature + encode_dss_signature ) from cryptography.utils import bit_length @@ -567,7 +567,7 @@ class TestDSAVerification(object): ), y=vector['y'] ).public_key(backend) - sig = encode_rfc6979_signature(vector['r'], vector['s']) + sig = encode_dss_signature(vector['r'], vector['s']) verifier = public_key.verifier(sig, algorithm()) verifier.update(vector['msg']) if vector['result'] == "F": |