From 1ade9eab8ebc8b1e9c98f945a1b5096c533c4de2 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 5 Jul 2024 18:13:06 -0400 Subject: [PATCH] Enforce length for CommonName fixes #10553 --- CHANGELOG.rst | 3 +++ src/cryptography/x509/name.py | 27 +++++++++++++++++---------- tests/x509/test_x509.py | 9 ++++++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 58a827719e652..8f8e14514a231 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -58,6 +58,9 @@ Changelog :meth:`~cryptography.x509.ocsp.OCSPSingleResponse.next_update_utc`, These are timezone-aware variants of existing properties that return naïve ``datetime`` objects. +* :class:`~cryptography.x509.NameAttribute` now raises an exception when + attempting to create a common name whose length is shorter or longer than + :rfc:`5280` permits. .. _v42-0-8: diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py index 451338a3a9300..1b6b89d12a97c 100644 --- a/src/cryptography/x509/name.py +++ b/src/cryptography/x509/name.py @@ -59,6 +59,12 @@ class _ASN1Type(utils.Enum): } _NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()} +_NAMEOID_LENGTH_LIMIT = { + NameOID.COUNTRY_NAME: (2, 2), + NameOID.JURISDICTION_COUNTRY_NAME: (2, 2), + NameOID.COMMON_NAME: (1, 64), +} + def _escape_dn_value(val: str | bytes) -> str: """Escape special characters in RFC4514 Distinguished Name value.""" @@ -132,19 +138,20 @@ def __init__( if not isinstance(value, str): raise TypeError("value argument must be a str") - if oid in (NameOID.COUNTRY_NAME, NameOID.JURISDICTION_COUNTRY_NAME): + length_limits = _NAMEOID_LENGTH_LIMIT.get(oid) + if length_limits is not None: + min_length, max_length = length_limits assert isinstance(value, str) c_len = len(value.encode("utf8")) - if c_len != 2 and _validate is True: - raise ValueError( - "Country name must be a 2 character country code" - ) - elif c_len != 2: - warnings.warn( - "Country names should be two characters, but the " - f"attribute is {c_len} characters in length.", - stacklevel=2, + if c_len < min_length or c_len > max_length: + msg = ( + f"Attribute's length must be >= {min_length} and " + f"<= {max_length}, but it was {c_len}" ) + if _validate is True: + raise ValueError(msg) + else: + warnings.warn(msg, stacklevel=2) # The appropriate ASN1 string type varies by OID and is defined across # multiple RFCs including 2459, 3280, and 5280. In general UTF8String diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 29e611d72901f..a4368833ca3f5 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -5809,7 +5809,7 @@ def test_init_none_value(self): None, # type:ignore[arg-type] ) - def test_init_bad_country_code_value(self): + def test_init_bad_length(self): with pytest.raises(ValueError): x509.NameAttribute(NameOID.COUNTRY_NAME, "United States") @@ -5817,6 +5817,13 @@ def test_init_bad_country_code_value(self): with pytest.raises(ValueError): x509.NameAttribute(NameOID.COUNTRY_NAME, "\U0001f37a\U0001f37a") + with pytest.raises(ValueError): + x509.NameAttribute(NameOID.JURISDICTION_COUNTRY_NAME, "Too Long") + with pytest.raises(ValueError): + x509.NameAttribute(NameOID.COMMON_NAME, "Too Long" * 10) + with pytest.raises(ValueError): + x509.NameAttribute(NameOID.COMMON_NAME, "") + def test_invalid_type(self): with pytest.raises(TypeError): x509.NameAttribute(