mirror of
https://github.com/ansible-collections/community.crypto.git
synced 2026-05-06 21:33:00 +00:00
Make Dirname (de)serialization conformant to RFC 4514 (#274)
* Adjust dirName serialization to RFC 4514. * Adjust deserialization to RFC 4514. * Add changelog fragment. * Use Unicode strings, and work around Python 2 and Python 3 differences and problems with old cryptography versions. * Work with bytes, not Unicode strings, to handle escaping of Unicode endpoints correctly.
This commit is contained in:
@@ -6,6 +6,12 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
import cryptography
|
||||
import pytest
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.basic import (
|
||||
@@ -14,11 +20,15 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.basic impo
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_support import (
|
||||
cryptography_get_name,
|
||||
_parse_dn_component,
|
||||
_parse_dn,
|
||||
)
|
||||
|
||||
from cryptography.x509 import NameAttribute, oid
|
||||
|
||||
|
||||
def test_cryptography_get_name_invalid_prefix():
|
||||
with pytest.raises(OpenSSLObjectError, match="Cannot parse Subject Alternative Name"):
|
||||
with pytest.raises(OpenSSLObjectError, match="^Cannot parse Subject Alternative Name"):
|
||||
cryptography_get_name('fake:value')
|
||||
|
||||
|
||||
@@ -31,3 +41,69 @@ def test_cryptography_get_name_other_name_utfstring():
|
||||
actual = cryptography_get_name('otherName:1.3.6.1.4.1.311.20.2.3;UTF8:Hello World')
|
||||
assert actual.type_id.dotted_string == '1.3.6.1.4.1.311.20.2.3'
|
||||
assert actual.value == b'\x0c\x0bHello World'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, options, expected', [
|
||||
(b'CN=x ', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'x '), b'')),
|
||||
(b'CN=\\ ', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u' '), b'')),
|
||||
(b'CN=\\#', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'#'), b'')),
|
||||
(b'CN=#402032', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'@ 2'), b'')),
|
||||
(b'CN = x ', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'x '), b'')),
|
||||
(b'CN = x\\, ', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'x, '), b'')),
|
||||
(b'CN = x\\40 ', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'x@ '), b'')),
|
||||
(b'CN = \\ , / ', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u' '), b', / ')),
|
||||
(b'CN = \\ , / ', {'sep': b'/'}, (NameAttribute(oid.NameOID.COMMON_NAME, u' , '), b'/ ')),
|
||||
(b'CN = \\ , / ', {'decode_remainder': False}, (NameAttribute(oid.NameOID.COMMON_NAME, u'\\ , / '), b'')),
|
||||
# Some examples from https://datatracker.ietf.org/doc/html/rfc4514#section-4:
|
||||
(b'CN=James \\"Jim\\" Smith\\, III', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'James "Jim" Smith, III'), b'')),
|
||||
(b'CN=Before\\0dAfter', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'Before\x0dAfter'), b'')),
|
||||
(b'1.3.6.1.4.1.1466.0=#04024869', {}, (NameAttribute(oid.ObjectIdentifier(u'1.3.6.1.4.1.1466.0'), u'\x04\x02Hi'), b'')),
|
||||
(b'CN=Lu\\C4\\8Di\\C4\\87', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u'Lučić'), b'')),
|
||||
])
|
||||
def test_parse_dn_component(name, options, expected):
|
||||
result = _parse_dn_component(name, **options)
|
||||
print(result, expected)
|
||||
assert result == expected
|
||||
|
||||
|
||||
# Cryptography < 2.9 does not allow empty strings
|
||||
# (https://github.com/pyca/cryptography/commit/87b2749c52e688c809f1861e55d958c64147493c)
|
||||
if LooseVersion(cryptography.__version__) >= LooseVersion('2.9'):
|
||||
@pytest.mark.parametrize('name, options, expected', [
|
||||
(b'CN=', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u''), b'')),
|
||||
(b'CN= ', {}, (NameAttribute(oid.NameOID.COMMON_NAME, u''), b'')),
|
||||
])
|
||||
def test_parse_dn_component_not_py26(name, options, expected):
|
||||
result = _parse_dn_component(name, **options)
|
||||
print(result, expected)
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, options, message', [
|
||||
(b'CN=\\0', {}, u'Hex escape sequence "\\0" incomplete at end of string'),
|
||||
(b'CN=\\0,', {}, u'Hex escape sequence "\\0," has invalid second letter'),
|
||||
(b'CN=#0,', {}, u'Invalid hex sequence entry "0,"'),
|
||||
])
|
||||
def test_parse_dn_component_failure(name, options, message):
|
||||
with pytest.raises(OpenSSLObjectError, match=u'^%s$' % re.escape(message)):
|
||||
result = _parse_dn_component(name, **options)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, expected', [
|
||||
(b'CN=foo', [NameAttribute(oid.NameOID.COMMON_NAME, u'foo')]),
|
||||
(b'CN=foo,CN=bar', [NameAttribute(oid.NameOID.COMMON_NAME, u'foo'), NameAttribute(oid.NameOID.COMMON_NAME, u'bar')]),
|
||||
(b'CN = foo , CN = bar', [NameAttribute(oid.NameOID.COMMON_NAME, u'foo '), NameAttribute(oid.NameOID.COMMON_NAME, u'bar')]),
|
||||
])
|
||||
def test_parse_dn(name, expected):
|
||||
result = _parse_dn(name)
|
||||
print(result, expected)
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, message', [
|
||||
(b'CN=\\0', u'Error while parsing distinguished name "CN=\\0": Hex escape sequence "\\0" incomplete at end of string'),
|
||||
(b'CN=x,', u'Error while parsing distinguished name "CN=x,": unexpected end of string'),
|
||||
])
|
||||
def test_parse_dn_failure(name, message):
|
||||
with pytest.raises(OpenSSLObjectError, match=u'^%s$' % re.escape(message)):
|
||||
result = _parse_dn(name)
|
||||
|
||||
Reference in New Issue
Block a user