aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/x509.rst98
-rw-r--r--src/cryptography/x509.py48
-rw-r--r--tests/test_x509_ext.py97
3 files changed, 243 insertions, 0 deletions
diff --git a/docs/x509.rst b/docs/x509.rst
index af249449..39df4a0b 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -447,6 +447,99 @@ X.509 Extensions
Returns an instance of the extension type corresponding to the OID.
+.. class:: KeyUsage
+
+ .. versionadded:: 0.9
+
+ The key usage extension defines the purpose of the key contained in the
+ certificate. The usage restriction might be employed when a key that could
+ be used for more than one operation is to be restricted. It corresponds to
+ :data:`OID_KEY_USAGE`.
+
+ .. attribute:: digital_signature
+
+ :type: bool
+
+ This is asserted when the subject public key is used for verifying
+ digital signatures, other than signatures on certificates
+ (``key_cert_sign``) and CRLs (``crl_sign``).
+
+ .. attribute:: content_commitment
+
+ :type: bool
+
+ This is asserted when the subject public key is used for verifying
+ digital signatures, other than signatures on certificates
+ (``key_cert_sign``) and CRLs (``crl_sign``). It is used to provide a
+ non-repudiation service that protects against the signing entity
+ falsely denying some action. In the case of later conflict, a
+ reliable third party may determine the authenticity of the signed
+ data. This was called ``non_repudiation`` in older revisions of the
+ X.509 specification.
+
+ .. attribute:: key_encipherment
+
+ :type: bool
+
+ This is asserted when the subject public key is used for enciphering
+ private or secret keys.
+
+ .. attribute:: data_encipherment
+
+ :type: bool
+
+ This is asserted when the subject public key is used for directly
+ enciphering raw user data without the use of an intermediate symmetric
+ cipher.
+
+ .. attribute:: key_agreement
+
+ :type: bool
+
+ This is asserted when the subject public key is used for key agreement.
+ For example, when a Diffie-Hellman key is to be used for key
+ management, then this bit is set.
+
+ .. attribute:: key_cert_sign
+
+ :type: bool
+
+ This is asserted when the subject public key is used for verifying
+ signatures on public key certificates. If this bit is asserted then
+ ``ca`` must be true in the :class:`BasicConstraints` extension.
+
+ .. attribute:: crl_sign
+
+ :type: bool
+
+ This is asserted when the subject public key is used for verifying
+ signatures on certificate revocation lists.
+
+ .. attribute:: encipher_only
+
+ :type: bool
+
+ The meaning of this bit is undefined in the absence of the
+ ``key_agreement`` bit. When this bit is asserted and the
+ ``key_agreement`` bit is also set, the subject public key may be
+ used only for enciphering data while performing key agreement.
+
+ :raises ValueError: This is raised if accessed when ``key_agreement``
+ is false.
+
+ .. attribute:: decipher_only
+
+ :type: bool
+
+ The meaning of this bit is undefined in the absence of the
+ ``key_agreement`` bit. When this bit is asserted and the
+ ``key_agreement`` bit is also set, the subject public key may be
+ used only for deciphering data while performing key agreement.
+
+ :raises ValueError: This is raised if accessed when ``key_agreement``
+ is false.
+
+
.. class:: BasicConstraints
.. versionadded:: 0.9
@@ -687,6 +780,11 @@ Extension OIDs
Corresponds to the dotted string ``"2.5.29.19"``. The identifier for the
:class:`BasicConstraints` extension type.
+.. data:: OID_KEY_USAGE
+
+ Corresponds to the dotted string ``"2.5.29.15"``. The identifier for the
+ :class:`KeyUsage` extension type.
+
Exceptions
~~~~~~~~~~
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 791d1ef0..b48a04dd 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -266,6 +266,54 @@ class BasicConstraints(object):
"path_length={0.path_length})>").format(self)
+class KeyUsage(object):
+ def __init__(self, digital_signature, content_commitment, key_encipherment,
+ data_encipherment, key_agreement, key_cert_sign, crl_sign,
+ encipher_only, decipher_only):
+ if not key_agreement and (encipher_only or decipher_only):
+ raise ValueError(
+ "encipher_only and decipher_only can only be true when "
+ "key_agreement is true"
+ )
+
+ self._digital_signature = digital_signature
+ self._content_commitment = content_commitment
+ self._key_encipherment = key_encipherment
+ self._data_encipherment = data_encipherment
+ self._key_agreement = key_agreement
+ self._key_cert_sign = key_cert_sign
+ self._crl_sign = crl_sign
+ self._encipher_only = encipher_only
+ self._decipher_only = decipher_only
+
+ digital_signature = utils.read_only_property("_digital_signature")
+ content_commitment = utils.read_only_property("_content_commitment")
+ key_encipherment = utils.read_only_property("_key_encipherment")
+ data_encipherment = utils.read_only_property("_data_encipherment")
+ key_agreement = utils.read_only_property("_key_agreement")
+ key_cert_sign = utils.read_only_property("_key_cert_sign")
+ crl_sign = utils.read_only_property("_crl_sign")
+
+ @property
+ def encipher_only(self):
+ if not self.key_agreement:
+ raise ValueError(
+ "encipher_only is undefined unless key_agreement is true"
+ )
+ else:
+ return self._encipher_only
+
+ @property
+ def decipher_only(self):
+ if not self.key_agreement:
+ raise ValueError(
+ "decipher_only is undefined unless key_agreement is true"
+ )
+ else:
+ return self._decipher_only
+
+
+
OID_COMMON_NAME = ObjectIdentifier("2.5.4.3")
OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7")
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 87580a0d..7447ac4b 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -35,6 +35,103 @@ class TestExtension(object):
)
+class TestKeyUsage(object):
+ def test_key_agreement_false_encipher_decipher_true(self):
+ with pytest.raises(ValueError):
+ x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=True,
+ decipher_only=False
+ )
+
+ with pytest.raises(ValueError):
+ x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=True,
+ decipher_only=True
+ )
+
+ with pytest.raises(ValueError):
+ x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=False,
+ decipher_only=True
+ )
+
+ def test_properties_key_agreement_true(self):
+ ku = x509.KeyUsage(
+ digital_signature=True,
+ content_commitment=True,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=True,
+ crl_sign=False,
+ encipher_only=False,
+ decipher_only=False
+ )
+ assert ku.digital_signature is True
+ assert ku.content_commitment is True
+ assert ku.key_encipherment is False
+ assert ku.data_encipherment is False
+ assert ku.key_agreement is False
+ assert ku.key_cert_sign is True
+ assert ku.crl_sign is False
+
+ def test_key_agreement_true_properties(self):
+ ku = x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=True,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=False,
+ decipher_only=True
+ )
+ assert ku.key_agreement is True
+ assert ku.encipher_only is False
+ assert ku.decipher_only is True
+
+ def test_key_agreement_false_properties(self):
+ ku = x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=False,
+ decipher_only=False
+ )
+ assert ku.key_agreement is False
+ with pytest.raises(ValueError):
+ ku.encipher_only
+
+ with pytest.raises(ValueError):
+ ku.decipher_only
+
+
class TestBasicConstraints(object):
def test_ca_not_boolean(self):
with pytest.raises(TypeError):