Reformat everything with black.

I had to undo the u string prefix removals to not drop Python 2 compatibility.
That's why black isn't enabled in antsibull-nox.toml yet.
This commit is contained in:
Felix Fontein
2025-04-28 09:51:33 +02:00
parent 04a0d38e3b
commit aec1826c34
118 changed files with 11780 additions and 7565 deletions

View File

@@ -38,6 +38,7 @@ try:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
_HAS_CRYPTOGRAPHY = True
except ImportError:
_HAS_CRYPTOGRAPHY = False
@@ -112,10 +113,12 @@ from .basic import (
CRYPTOGRAPHY_TIMEZONE = False
if _HAS_CRYPTOGRAPHY:
CRYPTOGRAPHY_TIMEZONE = LooseVersion(cryptography.__version__) >= LooseVersion('42.0.0')
CRYPTOGRAPHY_TIMEZONE = LooseVersion(cryptography.__version__) >= LooseVersion(
"42.0.0"
)
DOTTED_OID = re.compile(r'^\d+(?:\.\d+)+$')
DOTTED_OID = re.compile(r"^\d+(?:\.\d+)+$")
def cryptography_get_extensions_from_cert(cert):
@@ -150,7 +153,11 @@ def cryptography_get_extensions_from_cert(cert):
value=to_native(base64.b64encode(der)),
)
try:
oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
oid = obj2txt(
backend._lib,
backend._ffi,
backend._lib.X509_EXTENSION_get_object(ext),
)
except AttributeError:
oid = exts[i].oid.dotted_string
result[oid] = entry
@@ -189,8 +196,10 @@ def cryptography_get_extensions_from_csr(csr):
extensions,
lambda ext: backend._lib.sk_X509_EXTENSION_pop_free(
ext,
backend._ffi.addressof(backend._lib._original_lib, "X509_EXTENSION_free")
)
backend._ffi.addressof(
backend._lib._original_lib, "X509_EXTENSION_free"
),
),
)
# With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does
@@ -210,7 +219,11 @@ def cryptography_get_extensions_from_csr(csr):
value=to_native(base64.b64encode(der)),
)
try:
oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
oid = obj2txt(
backend._lib,
backend._ffi,
backend._lib.X509_EXTENSION_get_object(ext),
)
except AttributeError:
oid = exts[i].oid.dotted_string
result[oid] = entry
@@ -246,7 +259,7 @@ def cryptography_oid_to_name(oid, short=False):
name = names[0]
else:
name = oid._name
if name == 'Unknown OID':
if name == "Unknown OID":
name = dotted_string
if short:
return NORMALIZE_NAMES_SHORT.get(name, name)
@@ -258,104 +271,128 @@ def _get_hex(bytesstr):
if bytesstr is None:
return bytesstr
data = binascii.hexlify(bytesstr)
data = to_text(b':'.join(data[i:i + 2] for i in range(0, len(data), 2)))
data = to_text(b":".join(data[i : i + 2] for i in range(0, len(data), 2)))
return data
def _parse_hex(bytesstr):
if bytesstr is None:
return bytesstr
data = ''.join([('0' * (2 - len(p)) + p) if len(p) < 2 else p for p in to_text(bytesstr).split(':')])
data = "".join(
[
("0" * (2 - len(p)) + p) if len(p) < 2 else p
for p in to_text(bytesstr).split(":")
]
)
data = binascii.unhexlify(data)
return data
DN_COMPONENT_START_RE = re.compile(b'^ *([a-zA-z0-9.]+) *= *')
DN_HEX_LETTER = b'0123456789abcdef'
DN_COMPONENT_START_RE = re.compile(b"^ *([a-zA-z0-9.]+) *= *")
DN_HEX_LETTER = b"0123456789abcdef"
if sys.version_info[0] < 3:
_int_to_byte = chr
else:
def _int_to_byte(value):
return bytes((value, ))
return bytes((value,))
def _parse_dn_component(name, sep=b',', decode_remainder=True):
def _parse_dn_component(name, sep=b",", decode_remainder=True):
m = DN_COMPONENT_START_RE.match(name)
if not m:
raise OpenSSLObjectError(u'cannot start part in "{0}"'.format(to_text(name)))
oid = cryptography_name_to_oid(to_text(m.group(1)))
idx = len(m.group(0))
decoded_name = []
sep_str = sep + b'\\'
sep_str = sep + b"\\"
if decode_remainder:
length = len(name)
if length > idx and name[idx:idx + 1] == b'#':
if length > idx and name[idx : idx + 1] == b"#":
# Decoding a hex string
idx += 1
while idx + 1 < length:
ch1 = name[idx:idx + 1]
ch2 = name[idx + 1:idx + 2]
ch1 = name[idx : idx + 1]
ch2 = name[idx + 1 : idx + 2]
idx1 = DN_HEX_LETTER.find(ch1.lower())
idx2 = DN_HEX_LETTER.find(ch2.lower())
if idx1 < 0 or idx2 < 0:
raise OpenSSLObjectError(u'Invalid hex sequence entry "{0}"'.format(to_text(ch1 + ch2)))
raise OpenSSLObjectError(
u'Invalid hex sequence entry "{0}"'.format(to_text(ch1 + ch2))
)
idx += 2
decoded_name.append(_int_to_byte(idx1 * 16 + idx2))
else:
# Decoding a regular string
while idx < length:
i = idx
while i < length and name[i:i + 1] not in sep_str:
while i < length and name[i : i + 1] not in sep_str:
i += 1
if i > idx:
decoded_name.append(name[idx:i])
idx = i
while idx + 1 < length and name[idx:idx + 1] == b'\\':
ch = name[idx + 1:idx + 2]
while idx + 1 < length and name[idx : idx + 1] == b"\\":
ch = name[idx + 1 : idx + 2]
idx1 = DN_HEX_LETTER.find(ch.lower())
if idx1 >= 0:
if idx + 2 >= length:
raise OpenSSLObjectError(u'Hex escape sequence "\\{0}" incomplete at end of string'.format(to_text(ch)))
ch2 = name[idx + 2:idx + 3]
raise OpenSSLObjectError(
u'Hex escape sequence "\\{0}" incomplete at end of string'.format(
to_text(ch)
)
)
ch2 = name[idx + 2 : idx + 3]
idx2 = DN_HEX_LETTER.find(ch2.lower())
if idx2 < 0:
raise OpenSSLObjectError(u'Hex escape sequence "\\{0}" has invalid second letter'.format(to_text(ch + ch2)))
raise OpenSSLObjectError(
u'Hex escape sequence "\\{0}" has invalid second letter'.format(
to_text(ch + ch2)
)
)
ch = _int_to_byte(idx1 * 16 + idx2)
idx += 1
idx += 2
decoded_name.append(ch)
if idx < length and name[idx:idx + 1] == sep:
if idx < length and name[idx : idx + 1] == sep:
break
else:
decoded_name.append(name[idx:])
idx = len(name)
return x509.NameAttribute(oid, to_text(b''.join(decoded_name))), name[idx:]
return x509.NameAttribute(oid, to_text(b"".join(decoded_name))), name[idx:]
def _parse_dn(name):
'''
"""
Parse a Distinguished Name.
Can be of the form ``CN=Test, O = Something`` or ``CN = Test,O= Something``.
'''
"""
original_name = name
name = name.lstrip()
sep = b','
if name.startswith(b'/'):
sep = b'/'
sep = b","
if name.startswith(b"/"):
sep = b"/"
name = name[1:]
result = []
while name:
try:
attribute, name = _parse_dn_component(name, sep=sep)
except OpenSSLObjectError as e:
raise OpenSSLObjectError(u'Error while parsing distinguished name "{0}": {1}'.format(to_text(original_name), e))
raise OpenSSLObjectError(
u'Error while parsing distinguished name "{0}": {1}'.format(
to_text(original_name), e
)
)
result.append(attribute)
if name:
if name[0:1] != sep or len(name) < 2:
raise OpenSSLObjectError(u'Error while parsing distinguished name "{0}": unexpected end of string'.format(to_text(original_name)))
raise OpenSSLObjectError(
u'Error while parsing distinguished name "{0}": unexpected end of string'.format(
to_text(original_name)
)
)
name = name[1:]
return result
@@ -366,12 +403,16 @@ def cryptography_parse_relative_distinguished_name(rdn):
try:
names.append(_parse_dn_component(to_bytes(part), decode_remainder=False)[0])
except OpenSSLObjectError as e:
raise OpenSSLObjectError(u'Error while parsing relative distinguished name "{0}": {1}'.format(part, e))
raise OpenSSLObjectError(
u'Error while parsing relative distinguished name "{0}": {1}'.format(
part, e
)
)
return cryptography.x509.RelativeDistinguishedName(names)
def _is_ascii(value):
'''Check whether the Unicode string `value` contains only ASCII characters.'''
"""Check whether the Unicode string `value` contains only ASCII characters."""
try:
value.encode("ascii")
return True
@@ -380,195 +421,244 @@ def _is_ascii(value):
def _adjust_idn(value, idn_rewrite):
if idn_rewrite == 'ignore' or not value:
if idn_rewrite == "ignore" or not value:
return value
if idn_rewrite == 'idna' and _is_ascii(value):
if idn_rewrite == "idna" and _is_ascii(value):
return value
if idn_rewrite not in ('idna', 'unicode'):
if idn_rewrite not in ("idna", "unicode"):
raise ValueError('Invalid value for idn_rewrite: "{0}"'.format(idn_rewrite))
if not HAS_IDNA:
raise OpenSSLObjectError(
missing_required_lib('idna', reason='to transform {what} DNS name "{name}" to {dest}'.format(
name=value,
what='IDNA' if idn_rewrite == 'unicode' else 'Unicode',
dest='Unicode' if idn_rewrite == 'unicode' else 'IDNA',
)))
missing_required_lib(
"idna",
reason='to transform {what} DNS name "{name}" to {dest}'.format(
name=value,
what="IDNA" if idn_rewrite == "unicode" else "Unicode",
dest="Unicode" if idn_rewrite == "unicode" else "IDNA",
),
)
)
# Since IDNA does not like '*' or empty labels (except one empty label at the end),
# we split and let IDNA only handle labels that are neither empty or '*'.
parts = value.split(u'.')
parts = value.split(u".")
for index, part in enumerate(parts):
if part in (u'', u'*'):
if part in (u"", u"*"):
continue
try:
if idn_rewrite == 'idna':
parts[index] = idna.encode(part).decode('ascii')
elif idn_rewrite == 'unicode' and part.startswith(u'xn--'):
if idn_rewrite == "idna":
parts[index] = idna.encode(part).decode("ascii")
elif idn_rewrite == "unicode" and part.startswith(u"xn--"):
parts[index] = idna.decode(part)
except idna.IDNAError as exc2008:
try:
if idn_rewrite == 'idna':
parts[index] = part.encode('idna').decode('ascii')
elif idn_rewrite == 'unicode' and part.startswith(u'xn--'):
parts[index] = part.encode('ascii').decode('idna')
if idn_rewrite == "idna":
parts[index] = part.encode("idna").decode("ascii")
elif idn_rewrite == "unicode" and part.startswith(u"xn--"):
parts[index] = part.encode("ascii").decode("idna")
except Exception as exc2003:
raise OpenSSLObjectError(
u'Error while transforming part "{part}" of {what} DNS name "{name}" to {dest}.'
u' IDNA2008 transformation resulted in "{exc2008}", IDNA2003 transformation resulted in "{exc2003}".'.format(
part=part,
name=value,
what='IDNA' if idn_rewrite == 'unicode' else 'Unicode',
dest='Unicode' if idn_rewrite == 'unicode' else 'IDNA',
what="IDNA" if idn_rewrite == "unicode" else "Unicode",
dest="Unicode" if idn_rewrite == "unicode" else "IDNA",
exc2003=exc2003,
exc2008=exc2008,
))
return u'.'.join(parts)
)
)
return u".".join(parts)
def _adjust_idn_email(value, idn_rewrite):
idx = value.find(u'@')
idx = value.find(u"@")
if idx < 0:
return value
return u'{0}@{1}'.format(value[:idx], _adjust_idn(value[idx + 1:], idn_rewrite))
return u"{0}@{1}".format(value[:idx], _adjust_idn(value[idx + 1 :], idn_rewrite))
def _adjust_idn_url(value, idn_rewrite):
url = urlparse(value)
host = _adjust_idn(url.hostname, idn_rewrite)
if url.username is not None and url.password is not None:
host = u'{0}:{1}@{2}'.format(url.username, url.password, host)
host = u"{0}:{1}@{2}".format(url.username, url.password, host)
elif url.username is not None:
host = u'{0}@{1}'.format(url.username, host)
host = u"{0}@{1}".format(url.username, host)
if url.port is not None:
host = u'{0}:{1}'.format(host, url.port)
host = u"{0}:{1}".format(host, url.port)
return urlunparse(
ParseResult(scheme=url.scheme, netloc=host, path=url.path, params=url.params, query=url.query, fragment=url.fragment))
ParseResult(
scheme=url.scheme,
netloc=host,
path=url.path,
params=url.params,
query=url.query,
fragment=url.fragment,
)
)
def cryptography_get_name(name, what='Subject Alternative Name'):
'''
def cryptography_get_name(name, what="Subject Alternative Name"):
"""
Given a name string, returns a cryptography x509.GeneralName object.
Raises an OpenSSLObjectError if the name is unknown or cannot be parsed.
'''
"""
try:
if name.startswith('DNS:'):
return x509.DNSName(_adjust_idn(to_text(name[4:]), 'idna'))
if name.startswith('IP:'):
if name.startswith("DNS:"):
return x509.DNSName(_adjust_idn(to_text(name[4:]), "idna"))
if name.startswith("IP:"):
address = to_text(name[3:])
if '/' in address:
if "/" in address:
return x509.IPAddress(ipaddress.ip_network(address))
return x509.IPAddress(ipaddress.ip_address(address))
if name.startswith('email:'):
return x509.RFC822Name(_adjust_idn_email(to_text(name[6:]), 'idna'))
if name.startswith('URI:'):
return x509.UniformResourceIdentifier(_adjust_idn_url(to_text(name[4:]), 'idna'))
if name.startswith('RID:'):
m = re.match(r'^([0-9]+(?:\.[0-9]+)*)$', to_text(name[4:]))
if name.startswith("email:"):
return x509.RFC822Name(_adjust_idn_email(to_text(name[6:]), "idna"))
if name.startswith("URI:"):
return x509.UniformResourceIdentifier(
_adjust_idn_url(to_text(name[4:]), "idna")
)
if name.startswith("RID:"):
m = re.match(r"^([0-9]+(?:\.[0-9]+)*)$", to_text(name[4:]))
if not m:
raise OpenSSLObjectError('Cannot parse {what} "{name}"'.format(name=name, what=what))
raise OpenSSLObjectError(
'Cannot parse {what} "{name}"'.format(name=name, what=what)
)
return x509.RegisteredID(x509.oid.ObjectIdentifier(m.group(1)))
if name.startswith('otherName:'):
if name.startswith("otherName:"):
# otherName can either be a raw ASN.1 hex string or in the format that OpenSSL works with.
m = re.match(r'^([0-9]+(?:\.[0-9]+)*);([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2})*)$', to_text(name[10:]))
m = re.match(
r"^([0-9]+(?:\.[0-9]+)*);([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2})*)$",
to_text(name[10:]),
)
if m:
return x509.OtherName(x509.oid.ObjectIdentifier(m.group(1)), _parse_hex(m.group(2)))
return x509.OtherName(
x509.oid.ObjectIdentifier(m.group(1)), _parse_hex(m.group(2))
)
# See https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html - Subject Alternative Name for more
# defailts on the format expected.
name = to_text(name[10:], errors='surrogate_or_strict')
if ';' not in name:
raise OpenSSLObjectError('Cannot parse {what} otherName "{name}", must be in the '
'format "otherName:<OID>;<ASN.1 OpenSSL Encoded String>" or '
'"otherName:<OID>;<hex string>"'.format(name=name, what=what))
name = to_text(name[10:], errors="surrogate_or_strict")
if ";" not in name:
raise OpenSSLObjectError(
'Cannot parse {what} otherName "{name}", must be in the '
'format "otherName:<OID>;<ASN.1 OpenSSL Encoded String>" or '
'"otherName:<OID>;<hex string>"'.format(name=name, what=what)
)
oid, value = name.split(';', 1)
oid, value = name.split(";", 1)
b_value = serialize_asn1_string_as_der(value)
return x509.OtherName(x509.ObjectIdentifier(oid), b_value)
if name.startswith('dirName:'):
return x509.DirectoryName(x509.Name(reversed(_parse_dn(to_bytes(name[8:])))))
if name.startswith("dirName:"):
return x509.DirectoryName(
x509.Name(reversed(_parse_dn(to_bytes(name[8:]))))
)
except Exception as e:
raise OpenSSLObjectError('Cannot parse {what} "{name}": {error}'.format(name=name, what=what, error=e))
if ':' not in name:
raise OpenSSLObjectError('Cannot parse {what} "{name}" (forgot "DNS:" prefix?)'.format(name=name, what=what))
raise OpenSSLObjectError('Cannot parse {what} "{name}" (potentially unsupported by cryptography backend)'.format(name=name, what=what))
raise OpenSSLObjectError(
'Cannot parse {what} "{name}": {error}'.format(
name=name, what=what, error=e
)
)
if ":" not in name:
raise OpenSSLObjectError(
'Cannot parse {what} "{name}" (forgot "DNS:" prefix?)'.format(
name=name, what=what
)
)
raise OpenSSLObjectError(
'Cannot parse {what} "{name}" (potentially unsupported by cryptography backend)'.format(
name=name, what=what
)
)
def _dn_escape_value(value):
'''
"""
Escape Distinguished Name's attribute value.
'''
value = value.replace(u'\\', u'\\\\')
for ch in [u',', u'+', u'<', u'>', u';', u'"']:
value = value.replace(ch, u'\\%s' % ch)
value = value.replace(u'\0', u'\\00')
if value.startswith((u' ', u'#')):
value = u'\\%s' % value[0] + value[1:]
if value.endswith(u' '):
value = value[:-1] + u'\\ '
"""
value = value.replace(u"\\", u"\\\\")
for ch in [u",", u"+", u"<", u">", u";", u'"']:
value = value.replace(ch, u"\\%s" % ch)
value = value.replace(u"\0", u"\\00")
if value.startswith((u" ", u"#")):
value = u"\\%s" % value[0] + value[1:]
if value.endswith(u" "):
value = value[:-1] + u"\\ "
return value
def cryptography_decode_name(name, idn_rewrite='ignore'):
'''
def cryptography_decode_name(name, idn_rewrite="ignore"):
"""
Given a cryptography x509.GeneralName object, returns a string.
Raises an OpenSSLObjectError if the name is not supported.
'''
if idn_rewrite not in ('ignore', 'idna', 'unicode'):
raise AssertionError('idn_rewrite must be one of "ignore", "idna", or "unicode"')
"""
if idn_rewrite not in ("ignore", "idna", "unicode"):
raise AssertionError(
'idn_rewrite must be one of "ignore", "idna", or "unicode"'
)
if isinstance(name, x509.DNSName):
return u'DNS:{0}'.format(_adjust_idn(name.value, idn_rewrite))
return u"DNS:{0}".format(_adjust_idn(name.value, idn_rewrite))
if isinstance(name, x509.IPAddress):
if isinstance(name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)):
return u'IP:{0}/{1}'.format(name.value.network_address.compressed, name.value.prefixlen)
return u'IP:{0}'.format(name.value.compressed)
return u"IP:{0}/{1}".format(
name.value.network_address.compressed, name.value.prefixlen
)
return u"IP:{0}".format(name.value.compressed)
if isinstance(name, x509.RFC822Name):
return u'email:{0}'.format(_adjust_idn_email(name.value, idn_rewrite))
return u"email:{0}".format(_adjust_idn_email(name.value, idn_rewrite))
if isinstance(name, x509.UniformResourceIdentifier):
return u'URI:{0}'.format(_adjust_idn_url(name.value, idn_rewrite))
return u"URI:{0}".format(_adjust_idn_url(name.value, idn_rewrite))
if isinstance(name, x509.DirectoryName):
# According to https://datatracker.ietf.org/doc/html/rfc4514.html#section-2.1 the
# list needs to be reversed, and joined by commas
return u'dirName:' + ','.join([
u'{0}={1}'.format(to_text(cryptography_oid_to_name(attribute.oid, short=True)), _dn_escape_value(attribute.value))
for attribute in reversed(list(name.value))
])
return u"dirName:" + ",".join(
[
u"{0}={1}".format(
to_text(cryptography_oid_to_name(attribute.oid, short=True)),
_dn_escape_value(attribute.value),
)
for attribute in reversed(list(name.value))
]
)
if isinstance(name, x509.RegisteredID):
return u'RID:{0}'.format(name.value.dotted_string)
return u"RID:{0}".format(name.value.dotted_string)
if isinstance(name, x509.OtherName):
return u'otherName:{0};{1}'.format(name.type_id.dotted_string, _get_hex(name.value))
return u"otherName:{0};{1}".format(
name.type_id.dotted_string, _get_hex(name.value)
)
raise OpenSSLObjectError('Cannot decode name "{0}"'.format(name))
def _cryptography_get_keyusage(usage):
'''
"""
Given a key usage identifier string, returns the parameter name used by cryptography's x509.KeyUsage().
Raises an OpenSSLObjectError if the identifier is unknown.
'''
if usage in ('Digital Signature', 'digitalSignature'):
return 'digital_signature'
if usage in ('Non Repudiation', 'nonRepudiation'):
return 'content_commitment'
if usage in ('Key Encipherment', 'keyEncipherment'):
return 'key_encipherment'
if usage in ('Data Encipherment', 'dataEncipherment'):
return 'data_encipherment'
if usage in ('Key Agreement', 'keyAgreement'):
return 'key_agreement'
if usage in ('Certificate Sign', 'keyCertSign'):
return 'key_cert_sign'
if usage in ('CRL Sign', 'cRLSign'):
return 'crl_sign'
if usage in ('Encipher Only', 'encipherOnly'):
return 'encipher_only'
if usage in ('Decipher Only', 'decipherOnly'):
return 'decipher_only'
"""
if usage in ("Digital Signature", "digitalSignature"):
return "digital_signature"
if usage in ("Non Repudiation", "nonRepudiation"):
return "content_commitment"
if usage in ("Key Encipherment", "keyEncipherment"):
return "key_encipherment"
if usage in ("Data Encipherment", "dataEncipherment"):
return "data_encipherment"
if usage in ("Key Agreement", "keyAgreement"):
return "key_agreement"
if usage in ("Certificate Sign", "keyCertSign"):
return "key_cert_sign"
if usage in ("CRL Sign", "cRLSign"):
return "crl_sign"
if usage in ("Encipher Only", "encipherOnly"):
return "encipher_only"
if usage in ("Decipher Only", "decipherOnly"):
return "decipher_only"
raise OpenSSLObjectError('Unknown key usage "{0}"'.format(usage))
def cryptography_parse_key_usage_params(usages):
'''
"""
Given a list of key usage identifier strings, returns the parameters for cryptography's x509.KeyUsage().
Raises an OpenSSLObjectError if an identifier is unknown.
'''
"""
params = dict(
digital_signature=False,
content_commitment=False,
@@ -586,40 +676,52 @@ def cryptography_parse_key_usage_params(usages):
def cryptography_get_basic_constraints(constraints):
'''
"""
Given a list of constraints, returns a tuple (ca, path_length).
Raises an OpenSSLObjectError if a constraint is unknown or cannot be parsed.
'''
"""
ca = False
path_length = None
if constraints:
for constraint in constraints:
if constraint.startswith('CA:'):
if constraint == 'CA:TRUE':
if constraint.startswith("CA:"):
if constraint == "CA:TRUE":
ca = True
elif constraint == 'CA:FALSE':
elif constraint == "CA:FALSE":
ca = False
else:
raise OpenSSLObjectError('Unknown basic constraint value "{0}" for CA'.format(constraint[3:]))
elif constraint.startswith('pathlen:'):
v = constraint[len('pathlen:'):]
raise OpenSSLObjectError(
'Unknown basic constraint value "{0}" for CA'.format(
constraint[3:]
)
)
elif constraint.startswith("pathlen:"):
v = constraint[len("pathlen:") :]
try:
path_length = int(v)
except Exception as e:
raise OpenSSLObjectError('Cannot parse path length constraint "{0}" ({1})'.format(v, e))
raise OpenSSLObjectError(
'Cannot parse path length constraint "{0}" ({1})'.format(v, e)
)
else:
raise OpenSSLObjectError('Unknown basic constraint "{0}"'.format(constraint))
raise OpenSSLObjectError(
'Unknown basic constraint "{0}"'.format(constraint)
)
return ca, path_length
def cryptography_key_needs_digest_for_signing(key):
'''Tests whether the given private key requires a digest algorithm for signing.
"""Tests whether the given private key requires a digest algorithm for signing.
Ed25519 and Ed448 keys do not; they need None to be passed as the digest algorithm.
'''
if CRYPTOGRAPHY_HAS_ED25519 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey):
"""
if CRYPTOGRAPHY_HAS_ED25519 and isinstance(
key, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey
):
return False
if CRYPTOGRAPHY_HAS_ED448 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey):
if CRYPTOGRAPHY_HAS_ED448 and isinstance(
key, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey
):
return False
return True
@@ -637,16 +739,22 @@ def _compare_public_keys(key1, key2, clazz):
def cryptography_compare_public_keys(key1, key2):
'''Tests whether two public keys are the same.
"""Tests whether two public keys are the same.
Needs special logic for Ed25519 and Ed448 keys, since they do not have public_numbers().
'''
"""
if CRYPTOGRAPHY_HAS_ED25519:
res = _compare_public_keys(key1, key2, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey)
res = _compare_public_keys(
key1,
key2,
cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey,
)
if res is not None:
return res
if CRYPTOGRAPHY_HAS_ED448:
res = _compare_public_keys(key1, key2, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey)
res = _compare_public_keys(
key1, key2, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey
)
if res is not None:
return res
return key1.public_numbers() == key2.public_numbers()
@@ -663,41 +771,61 @@ def _compare_private_keys(key1, key2, clazz, has_no_private_bytes=False):
# We do not have the private_bytes() function - compare associated public keys
return cryptography_compare_public_keys(a.public_key(), b.public_key())
encryption_algorithm = cryptography.hazmat.primitives.serialization.NoEncryption()
a = key1.private_bytes(serialization.Encoding.Raw, serialization.PrivateFormat.Raw, encryption_algorithm=encryption_algorithm)
b = key2.private_bytes(serialization.Encoding.Raw, serialization.PrivateFormat.Raw, encryption_algorithm=encryption_algorithm)
a = key1.private_bytes(
serialization.Encoding.Raw,
serialization.PrivateFormat.Raw,
encryption_algorithm=encryption_algorithm,
)
b = key2.private_bytes(
serialization.Encoding.Raw,
serialization.PrivateFormat.Raw,
encryption_algorithm=encryption_algorithm,
)
return a == b
def cryptography_compare_private_keys(key1, key2):
'''Tests whether two private keys are the same.
"""Tests whether two private keys are the same.
Needs special logic for Ed25519, X25519, and Ed448 keys, since they do not have private_numbers().
'''
"""
if CRYPTOGRAPHY_HAS_ED25519:
res = _compare_private_keys(key1, key2, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey)
res = _compare_private_keys(
key1,
key2,
cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey,
)
if res is not None:
return res
if CRYPTOGRAPHY_HAS_X25519:
res = _compare_private_keys(
key1, key2, cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey, has_no_private_bytes=not CRYPTOGRAPHY_HAS_X25519_FULL)
key1,
key2,
cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey,
has_no_private_bytes=not CRYPTOGRAPHY_HAS_X25519_FULL,
)
if res is not None:
return res
if CRYPTOGRAPHY_HAS_ED448:
res = _compare_private_keys(key1, key2, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey)
res = _compare_private_keys(
key1, key2, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey
)
if res is not None:
return res
if CRYPTOGRAPHY_HAS_X448:
res = _compare_private_keys(key1, key2, cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey)
res = _compare_private_keys(
key1, key2, cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey
)
if res is not None:
return res
return key1.private_numbers() == key2.private_numbers()
def cryptography_serial_number_of_cert(cert):
'''Returns cert.serial_number.
"""Returns cert.serial_number.
Also works for old versions of cryptography.
'''
"""
try:
return cert.serial_number
except AttributeError:
@@ -706,10 +834,11 @@ def cryptography_serial_number_of_cert(cert):
def parse_pkcs12(pkcs12_bytes, passphrase=None):
'''Returns a tuple (private_key, certificate, additional_certificates, friendly_name).
'''
"""Returns a tuple (private_key, certificate, additional_certificates, friendly_name)."""
if _load_pkcs12 is None and _load_key_and_certificates is None:
raise ValueError('neither load_pkcs12() nor load_key_and_certificates() present in the current cryptography version')
raise ValueError(
"neither load_pkcs12() nor load_key_and_certificates() present in the current cryptography version"
)
if passphrase is not None:
passphrase = to_bytes(passphrase)
@@ -718,7 +847,7 @@ def parse_pkcs12(pkcs12_bytes, passphrase=None):
if _load_pkcs12 is not None:
return _parse_pkcs12_36_0_0(pkcs12_bytes, passphrase)
if LooseVersion(cryptography.__version__) >= LooseVersion('35.0'):
if LooseVersion(cryptography.__version__) >= LooseVersion("35.0"):
return _parse_pkcs12_35_0_0(pkcs12_bytes, passphrase)
return _parse_pkcs12_legacy(pkcs12_bytes, passphrase)
@@ -739,7 +868,9 @@ def _parse_pkcs12_36_0_0(pkcs12_bytes, passphrase=None):
def _parse_pkcs12_35_0_0(pkcs12_bytes, passphrase=None):
# Backwards compatibility code for cryptography 35.x
private_key, certificate, additional_certificates = _load_key_and_certificates(pkcs12_bytes, passphrase)
private_key, certificate, additional_certificates = _load_key_and_certificates(
pkcs12_bytes, passphrase
)
friendly_name = None
if certificate:
@@ -749,18 +880,26 @@ def _parse_pkcs12_35_0_0(pkcs12_bytes, passphrase=None):
# This code basically does what load_key_and_certificates() does, but without error-checking.
# Since load_key_and_certificates succeeded, it should not fail.
pkcs12 = backend._ffi.gc(
backend._lib.d2i_PKCS12_bio(backend._bytes_to_bio(pkcs12_bytes).bio, backend._ffi.NULL),
backend._lib.PKCS12_free)
backend._lib.d2i_PKCS12_bio(
backend._bytes_to_bio(pkcs12_bytes).bio, backend._ffi.NULL
),
backend._lib.PKCS12_free,
)
certificate_x509_ptr = backend._ffi.new("X509 **")
with backend._zeroed_null_terminated_buf(to_bytes(passphrase) if passphrase is not None else None) as passphrase_buffer:
with backend._zeroed_null_terminated_buf(
to_bytes(passphrase) if passphrase is not None else None
) as passphrase_buffer:
backend._lib.PKCS12_parse(
pkcs12,
passphrase_buffer,
backend._ffi.new("EVP_PKEY **"),
certificate_x509_ptr,
backend._ffi.new("Cryptography_STACK_OF_X509 **"))
backend._ffi.new("Cryptography_STACK_OF_X509 **"),
)
if certificate_x509_ptr[0] != backend._ffi.NULL:
maybe_name = backend._lib.X509_alias_get0(certificate_x509_ptr[0], backend._ffi.NULL)
maybe_name = backend._lib.X509_alias_get0(
certificate_x509_ptr[0], backend._ffi.NULL
)
if maybe_name != backend._ffi.NULL:
friendly_name = backend._ffi.string(maybe_name)
@@ -769,7 +908,9 @@ def _parse_pkcs12_35_0_0(pkcs12_bytes, passphrase=None):
def _parse_pkcs12_legacy(pkcs12_bytes, passphrase=None):
# Backwards compatibility code for cryptography < 35.0.0
private_key, certificate, additional_certificates = _load_key_and_certificates(pkcs12_bytes, passphrase)
private_key, certificate, additional_certificates = _load_key_and_certificates(
pkcs12_bytes, passphrase
)
friendly_name = None
if certificate:
@@ -782,39 +923,62 @@ def _parse_pkcs12_legacy(pkcs12_bytes, passphrase=None):
def cryptography_verify_signature(signature, data, hash_algorithm, signer_public_key):
'''
"""
Check whether the given signature of the given data was signed by the given public key object.
'''
"""
try:
if CRYPTOGRAPHY_HAS_RSA_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey):
signer_public_key.verify(signature, data, padding.PKCS1v15(), hash_algorithm)
if CRYPTOGRAPHY_HAS_RSA_SIGN and isinstance(
signer_public_key,
cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey,
):
signer_public_key.verify(
signature, data, padding.PKCS1v15(), hash_algorithm
)
return True
if CRYPTOGRAPHY_HAS_EC_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey):
signer_public_key.verify(signature, data, cryptography.hazmat.primitives.asymmetric.ec.ECDSA(hash_algorithm))
if CRYPTOGRAPHY_HAS_EC_SIGN and isinstance(
signer_public_key,
cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey,
):
signer_public_key.verify(
signature,
data,
cryptography.hazmat.primitives.asymmetric.ec.ECDSA(hash_algorithm),
)
return True
if CRYPTOGRAPHY_HAS_DSA_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey):
if CRYPTOGRAPHY_HAS_DSA_SIGN and isinstance(
signer_public_key,
cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey,
):
signer_public_key.verify(signature, data, hash_algorithm)
return True
if CRYPTOGRAPHY_HAS_ED25519_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey):
if CRYPTOGRAPHY_HAS_ED25519_SIGN and isinstance(
signer_public_key,
cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey,
):
signer_public_key.verify(signature, data)
return True
if CRYPTOGRAPHY_HAS_ED448_SIGN and isinstance(signer_public_key, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey):
if CRYPTOGRAPHY_HAS_ED448_SIGN and isinstance(
signer_public_key,
cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey,
):
signer_public_key.verify(signature, data)
return True
raise OpenSSLObjectError(u'Unsupported public key type {0}'.format(type(signer_public_key)))
raise OpenSSLObjectError(
u"Unsupported public key type {0}".format(type(signer_public_key))
)
except InvalidSignature:
return False
def cryptography_verify_certificate_signature(certificate, signer_public_key):
'''
"""
Check whether the given X509 certificate object was signed by the given public key object.
'''
"""
return cryptography_verify_signature(
certificate.signature,
certificate.tbs_certificate_bytes,
certificate.signature_hash_algorithm,
signer_public_key
signer_public_key,
)