Work on issues found by pylint (#896)

* Look at possibly-used-before-assignment.

* Use latest beta releases of ansible-core 2.19 for mypy and pylint.

* Look at unsupported-*.

* Look at unknown-option-value.

* Look at redefined-builtin.

* Look at superfluous-parens.

* Look at unspecified-encoding.

* Adjust to new cryptography version and to ansible-core 2.17's pylint.

* Look at super-with-arguments.

* Look at no-else-*.

* Look at try-except-raise.

* Look at inconsistent-return-statements.

* Look at redefined-outer-name.

* Look at redefined-argument-from-local.

* Look at attribute-defined-outside-init.

* Look at unused-variable.

* Look at protected-access.

* Look at raise-missing-from.

* Look at arguments-differ.

* Look at useless-suppression and use-symbolic-message-instead.

* Look at consider-using-dict-items.

* Look at consider-using-in.

* Look at consider-using-set-comprehension.

* Look at consider-using-with.

* Look at use-dict-literal.
This commit is contained in:
Felix Fontein
2025-05-18 00:57:28 +02:00
committed by GitHub
parent a3a5284f97
commit 318462fa24
96 changed files with 1748 additions and 1598 deletions

View File

@@ -12,9 +12,9 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto._objects_
)
OID_LOOKUP: dict[str, str] = dict()
NORMALIZE_NAMES: dict[str, str] = dict()
NORMALIZE_NAMES_SHORT: dict[str, str] = dict()
OID_LOOKUP: dict[str, str] = {}
NORMALIZE_NAMES: dict[str, str] = {}
NORMALIZE_NAMES_SHORT: dict[str, str] = {}
for dotted, names in OID_MAP.items():
for name in names:

View File

@@ -62,13 +62,13 @@ if HAS_CRYPTOGRAPHY:
"aa_compromise": x509.ReasonFlags.aa_compromise,
"remove_from_crl": x509.ReasonFlags.remove_from_crl,
}
REVOCATION_REASON_MAP_INVERSE = dict()
REVOCATION_REASON_MAP_INVERSE = {}
for k, v in REVOCATION_REASON_MAP.items():
REVOCATION_REASON_MAP_INVERSE[v] = k
else:
REVOCATION_REASON_MAP = dict()
REVOCATION_REASON_MAP_INVERSE = dict()
REVOCATION_REASON_MAP = {}
REVOCATION_REASON_MAP_INVERSE = {}
def cryptography_decode_revoked_certificate(
@@ -145,7 +145,9 @@ def cryptography_get_signature_algorithm_oid_from_crl(
except AttributeError:
# Older cryptography versions do not have signature_algorithm_oid yet
dotted = obj2txt(
crl._backend._lib, crl._backend._ffi, crl._x509_crl.sig_alg.algorithm # type: ignore
crl._backend._lib, # type: ignore[attr-defined] # pylint: disable=protected-access
crl._backend._ffi, # type: ignore[attr-defined] # pylint: disable=protected-access
crl._x509_crl.sig_alg.algorithm, # type: ignore[attr-defined] # pylint: disable=protected-access
)
return x509.oid.ObjectIdentifier(dotted)

View File

@@ -113,17 +113,17 @@ if t.TYPE_CHECKING:
PKCS12KeyAndCertificates,
)
CertificatePrivateKeyTypes = (
CertificateIssuerPrivateKeyTypes
| cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey
| cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey
)
PublicKeyTypesWOEdwards = (
DHPublicKey | DSAPublicKey | EllipticCurvePublicKey | RSAPublicKey
)
PrivateKeyTypesWOEdwards = (
DHPrivateKey | DSAPrivateKey | EllipticCurvePrivateKey | RSAPrivateKey
)
CertificatePrivateKeyTypes = t.Union[
CertificateIssuerPrivateKeyTypes,
cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey,
cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey,
]
PublicKeyTypesWOEdwards = t.Union[
DHPublicKey, DSAPublicKey, EllipticCurvePublicKey, RSAPublicKey
]
PrivateKeyTypesWOEdwards = t.Union[
DHPrivateKey, DSAPrivateKey, EllipticCurvePrivateKey, RSAPrivateKey
]
else:
PublicKeyTypesWOEdwards = None
PrivateKeyTypesWOEdwards = None
@@ -146,14 +146,14 @@ DOTTED_OID = re.compile(r"^\d+(?:\.\d+)+$")
def cryptography_get_extensions_from_cert(
cert: x509.Certificate,
) -> dict[str, dict[str, bool | str]]:
result = dict()
result = {}
if _CRYPTOGRAPHY_36_0_OR_NEWER:
for ext in cert.extensions:
result[ext.oid.dotted_string] = dict(
critical=ext.critical,
value=base64.b64encode(ext.value.public_bytes()).decode("ascii"),
)
result[ext.oid.dotted_string] = {
"critical": ext.critical,
"value": base64.b64encode(ext.value.public_bytes()).decode("ascii"),
}
else:
# Since cryptography will not give us the DER value for an extension
# (that is only stored for unrecognized extensions), we have to re-do
@@ -162,6 +162,9 @@ def cryptography_get_extensions_from_cert(
backend = default_backend()
# We access a *lot* of internal APIs here, so let's disable that message...
# pylint: disable=protected-access
x509_obj = cert._x509 # type: ignore
# With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does
# not allow to get the raw value of an extension, so we have to use this ugly hack:
@@ -175,10 +178,10 @@ def cryptography_get_extensions_from_cert(
data = backend._lib.X509_EXTENSION_get_data(ext)
backend.openssl_assert(data != backend._ffi.NULL)
der = backend._ffi.buffer(data.data, data.length)[:]
entry = dict(
critical=(crit == 1),
value=base64.b64encode(der).decode("ascii"),
)
entry = {
"critical": (crit == 1),
"value": base64.b64encode(der).decode("ascii"),
}
try:
oid = obj2txt(
backend._lib,
@@ -195,14 +198,14 @@ def cryptography_get_extensions_from_cert(
def cryptography_get_extensions_from_csr(
csr: x509.CertificateSigningRequest,
) -> dict[str, dict[str, bool | str]]:
result = dict()
result = {}
if _CRYPTOGRAPHY_36_0_OR_NEWER:
for ext in csr.extensions:
result[ext.oid.dotted_string] = dict(
critical=ext.critical,
value=base64.b64encode(ext.value.public_bytes()).decode("ascii"),
)
result[ext.oid.dotted_string] = {
"critical": ext.critical,
"value": base64.b64encode(ext.value.public_bytes()).decode("ascii"),
}
else:
# Since cryptography will not give us the DER value for an extension
@@ -212,6 +215,9 @@ def cryptography_get_extensions_from_csr(
backend = default_backend()
# We access a *lot* of internal APIs here, so let's disable that message...
# pylint: disable=protected-access
extensions = backend._lib.X509_REQ_get_extensions(csr._x509_req) # type: ignore
extensions = backend._ffi.gc(
extensions,
@@ -235,10 +241,10 @@ def cryptography_get_extensions_from_csr(
data = backend._lib.X509_EXTENSION_get_data(ext)
backend.openssl_assert(data != backend._ffi.NULL)
der: bytes = backend._ffi.buffer(data.data, data.length)[:] # type: ignore
entry = dict(
critical=(crit == 1),
value=base64.b64encode(der).decode("ascii"),
)
entry = {
"critical": (crit == 1),
"value": base64.b64encode(der).decode("ascii"),
}
try:
oid = obj2txt(
backend._lib,
@@ -269,13 +275,15 @@ def cryptography_oid_to_name(
if names:
name = names[0]
else:
name = oid._name
if name == "Unknown OID":
try:
name = oid._name # pylint: disable=protected-access
if name == "Unknown OID":
name = dotted_string
except AttributeError:
name = dotted_string
if short:
return NORMALIZE_NAMES_SHORT.get(name, name)
else:
return NORMALIZE_NAMES.get(name, name)
return NORMALIZE_NAMES.get(name, name)
def _get_hex(bytesstr: bytes) -> str:
@@ -393,7 +401,7 @@ def _parse_dn(name: bytes) -> list[x509.NameAttribute]:
except OpenSSLObjectError as e:
raise OpenSSLObjectError(
f"Error while parsing distinguished name {to_text(original_name)!r}: {e}"
)
) from e
result.append(attribute)
if name:
if name[0:1] != sep or len(name) < 2:
@@ -414,7 +422,7 @@ def cryptography_parse_relative_distinguished_name(
except OpenSSLObjectError as e:
raise OpenSSLObjectError(
f"Error while parsing relative distinguished name {to_text(part)!r}: {e}"
)
) from e
return cryptography.x509.RelativeDistinguishedName(names)
@@ -468,7 +476,7 @@ def _adjust_idn(
raise OpenSSLObjectError(
f'Error while transforming part "{part}" of {what} DNS name "{value}" to {dest}.'
f' IDNA2008 transformation resulted in "{exc2008}", IDNA2003 transformation resulted in "{exc2003}".'
)
) from exc2003
return ".".join(parts)
@@ -561,7 +569,7 @@ def cryptography_get_name(
x509.Name(reversed(_parse_dn(to_bytes(name[8:]))))
)
except Exception as e:
raise OpenSSLObjectError(f'Cannot parse {what} "{name}": {e}')
raise OpenSSLObjectError(f'Cannot parse {what} "{name}": {e}') from e
if ":" not in name:
raise OpenSSLObjectError(
f'Cannot parse {what} "{name}" (forgot "DNS:" prefix?)'
@@ -656,17 +664,17 @@ def cryptography_parse_key_usage_params(usages: t.Iterable[str]) -> dict[str, bo
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,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False,
)
params = {
"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,
}
for usage in usages:
params[_cryptography_get_keyusage(usage)] = True
return params
@@ -699,7 +707,7 @@ def cryptography_get_basic_constraints(
except Exception as e:
raise OpenSSLObjectError(
f'Cannot parse path length constraint "{v}" ({e})'
)
) from e
else:
raise OpenSSLObjectError(f'Unknown basic constraint "{constraint}"')
return ca, path_length
@@ -901,6 +909,9 @@ def _parse_pkcs12_35_0_0(
backend = default_backend()
# We access a *lot* of internal APIs here, so let's disable that message...
# pylint: disable=protected-access
# 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(
@@ -944,6 +955,9 @@ def _parse_pkcs12_legacy(
pkcs12_bytes, passphrase
)
# We access a *lot* of internal APIs here, so let's disable that message...
# pylint: disable=protected-access
friendly_name = None
if certificate:
# See https://github.com/pyca/cryptography/issues/5760#issuecomment-842687238

View File

@@ -109,7 +109,7 @@ class CertificateBackend(metaclass=abc.ABCMeta):
result["can_parse_certificate"] = True
return result
except Exception:
return dict(can_parse_certificate=False)
return {"can_parse_certificate": False}
@abc.abstractmethod
def generate_certificate(self) -> None:
@@ -143,7 +143,7 @@ class CertificateBackend(metaclass=abc.ABCMeta):
passphrase=self.privatekey_passphrase,
)
except OpenSSLBadPassphraseError as exc:
raise CertificateError(exc)
raise CertificateError(exc) from exc
def _ensure_csr_loaded(self) -> None:
"""Load the CSR into self.csr."""
@@ -380,25 +380,28 @@ def select_backend(
def get_certificate_argument_spec() -> ArgumentSpec:
return ArgumentSpec(
argument_spec=dict(
provider=dict(
type="str", choices=[]
), # choices will be filled by add_XXX_provider_to_argument_spec() in certificate_xxx.py
force=dict(
type="bool",
default=False,
),
csr_path=dict(type="path"),
csr_content=dict(type="str"),
ignore_timestamps=dict(type="bool", default=True),
select_crypto_backend=dict(
type="str", default="auto", choices=["auto", "cryptography"]
),
argument_spec={
"provider": {
"type": "str",
"choices": [],
}, # choices will be filled by add_XXX_provider_to_argument_spec() in certificate_xxx.py
"force": {
"type": "bool",
"default": False,
},
"csr_path": {"type": "path"},
"csr_content": {"type": "str"},
"ignore_timestamps": {"type": "bool", "default": True},
"select_crypto_backend": {
"type": "str",
"default": "auto",
"choices": ["auto", "cryptography"],
},
# General properties of a certificate
privatekey_path=dict(type="path"),
privatekey_content=dict(type="str", no_log=True),
privatekey_passphrase=dict(type="str", no_log=True),
),
"privatekey_path": {"type": "path"},
"privatekey_content": {"type": "str", "no_log": True},
"privatekey_passphrase": {"type": "str", "no_log": True},
},
mutually_exclusive=[
["csr_path", "csr_content"],
["privatekey_path", "privatekey_content"],

View File

@@ -30,7 +30,7 @@ if t.TYPE_CHECKING:
class AcmeCertificateBackend(CertificateBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(AcmeCertificateBackend, self).__init__(module=module)
super().__init__(module=module)
self.accountkey_path: str = module.params["acme_accountkey_path"]
self.challenge_path: str = module.params["acme_challenge_path"]
self.use_chain: bool = module.params["acme_chain"]
@@ -94,7 +94,7 @@ class AcmeCertificateBackend(CertificateBackend):
self.module.run_command(command, check_rc=True)[1]
)
except OSError as exc:
raise CertificateError(exc)
raise CertificateError(exc) from exc
def get_certificate_data(self) -> bytes:
"""Return bytes for self.cert."""
@@ -103,9 +103,7 @@ class AcmeCertificateBackend(CertificateBackend):
return self.cert_bytes
def dump(self, *, include_certificate: bool) -> dict[str, t.Any]:
result = super(AcmeCertificateBackend, self).dump(
include_certificate=include_certificate
)
result = super().dump(include_certificate=include_certificate)
result["accountkey"] = self.accountkey_path
return result
@@ -128,14 +126,15 @@ class AcmeCertificateProvider(CertificateProvider):
def add_acme_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
argument_spec.argument_spec["provider"]["choices"].append("acme")
argument_spec.argument_spec.update(
dict(
acme_accountkey_path=dict(type="path"),
acme_challenge_path=dict(type="path"),
acme_chain=dict(type="bool", default=False),
acme_directory=dict(
type="str", default="https://acme-v02.api.letsencrypt.org/directory"
),
)
{
"acme_accountkey_path": {"type": "path"},
"acme_challenge_path": {"type": "path"},
"acme_chain": {"type": "bool", "default": False},
"acme_directory": {
"type": "str",
"default": "https://acme-v02.api.letsencrypt.org/directory",
},
}
)

View File

@@ -51,13 +51,14 @@ except ImportError:
class EntrustCertificateBackend(CertificateBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(EntrustCertificateBackend, self).__init__(module=module)
super().__init__(module=module)
self.trackingId = None
self.notAfter = get_relative_time_option(
module.params["entrust_not_after"],
input_name="entrust_not_after",
with_timezone=CRYPTOGRAPHY_TIMEZONE,
)
self.cert_bytes: bytes | None = None
if self.csr_content is None:
if self.csr_path is None:
@@ -119,7 +120,7 @@ class EntrustCertificateBackend(CertificateBackend):
body["csr"] = to_native(self.csr_content)
else:
assert self.csr_path is not None
with open(self.csr_path, "r") as csr_file:
with open(self.csr_path, "r", encoding="utf-8") as csr_file:
body["csr"] = csr_file.read()
body["certType"] = self.module.params["entrust_cert_type"]
@@ -157,6 +158,8 @@ class EntrustCertificateBackend(CertificateBackend):
def get_certificate_data(self) -> bytes:
"""Return bytes for self.cert."""
if self.cert_bytes is None:
raise AssertionError("Contract violation: cert_bytes not set")
return self.cert_bytes
def needs_regeneration(
@@ -165,7 +168,7 @@ class EntrustCertificateBackend(CertificateBackend):
not_before: datetime.datetime | None = None,
not_after: datetime.datetime | None = None,
) -> bool:
parent_check = super(EntrustCertificateBackend, self).needs_regeneration()
parent_check = super().needs_regeneration()
try:
cert_details = self._get_cert_details()
@@ -176,7 +179,7 @@ class EntrustCertificateBackend(CertificateBackend):
# Always issue a new certificate if the certificate is expired, suspended or revoked
status = cert_details.get("status", False)
if status == "EXPIRED" or status == "SUSPENDED" or status == "REVOKED":
if status in ("EXPIRED", "SUSPENDED", "REVOKED"):
return True
# If the requested cert type was specified and it is for a different certificate type than the initial certificate, a new one is needed
@@ -239,11 +242,11 @@ class EntrustCertificateProvider(CertificateProvider):
def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
argument_spec.argument_spec["provider"]["choices"].append("entrust")
argument_spec.argument_spec.update(
dict(
entrust_cert_type=dict(
type="str",
default="STANDARD_SSL",
choices=[
{
"entrust_cert_type": {
"type": "str",
"default": "STANDARD_SSL",
"choices": [
"STANDARD_SSL",
"ADVANTAGE_SSL",
"UC_SSL",
@@ -255,20 +258,20 @@ def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
"CDS_ENT_PRO",
"SMIME_ENT",
],
),
entrust_requester_email=dict(type="str"),
entrust_requester_name=dict(type="str"),
entrust_requester_phone=dict(type="str"),
entrust_api_user=dict(type="str"),
entrust_api_key=dict(type="str", no_log=True),
entrust_api_client_cert_path=dict(type="path"),
entrust_api_client_cert_key_path=dict(type="path", no_log=True),
entrust_api_specification_path=dict(
type="path",
default="https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml",
),
entrust_not_after=dict(type="str", default="+365d"),
)
},
"entrust_requester_email": {"type": "str"},
"entrust_requester_name": {"type": "str"},
"entrust_requester_phone": {"type": "str"},
"entrust_api_user": {"type": "str"},
"entrust_api_key": {"type": "str", "no_log": True},
"entrust_api_client_cert_path": {"type": "path"},
"entrust_api_client_cert_key_path": {"type": "path", "no_log": True},
"entrust_api_specification_path": {
"type": "path",
"default": "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml",
},
"entrust_not_after": {"type": "str", "default": "+365d"},
}
)
argument_spec.required_if.append(
(

View File

@@ -70,6 +70,8 @@ TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
class CertificateInfoRetrieval(metaclass=abc.ABCMeta):
cert: x509.Certificate
def __init__(self, *, module: GeneralAnsibleModule, content: bytes) -> None:
# content must be a bytes string
self.module = module
@@ -169,11 +171,11 @@ class CertificateInfoRetrieval(metaclass=abc.ABCMeta):
result["signature_algorithm"] = self._get_signature_algorithm()
subject = self._get_subject_ordered()
issuer = self._get_issuer_ordered()
result["subject"] = dict()
result["subject"] = {}
for k, v in subject:
result["subject"][k] = v
result["subject_ordered"] = subject
result["issuer"] = dict()
result["issuer"] = {}
for k, v in issuer:
result["issuer"][k] = v
result["issuer_ordered"] = issuer
@@ -249,9 +251,7 @@ class CertificateInfoRetrievalCryptography(CertificateInfoRetrieval):
"""Validate the supplied cert, using the cryptography backend"""
def __init__(self, *, module: GeneralAnsibleModule, content: bytes) -> None:
super(CertificateInfoRetrievalCryptography, self).__init__(
module=module, content=content
)
super().__init__(module=module, content=content)
self.name_encoding = module.params.get("name_encoding", "ignore")
def _get_der_bytes(self) -> bytes:
@@ -289,36 +289,36 @@ class CertificateInfoRetrievalCryptography(CertificateInfoRetrieval):
x509.KeyUsage
)
current_key_usage = current_key_ext.value
key_usage = dict(
digital_signature=current_key_usage.digital_signature,
content_commitment=current_key_usage.content_commitment,
key_encipherment=current_key_usage.key_encipherment,
data_encipherment=current_key_usage.data_encipherment,
key_agreement=current_key_usage.key_agreement,
key_cert_sign=current_key_usage.key_cert_sign,
crl_sign=current_key_usage.crl_sign,
encipher_only=False,
decipher_only=False,
)
key_usage = {
"digital_signature": current_key_usage.digital_signature,
"content_commitment": current_key_usage.content_commitment,
"key_encipherment": current_key_usage.key_encipherment,
"data_encipherment": current_key_usage.data_encipherment,
"key_agreement": current_key_usage.key_agreement,
"key_cert_sign": current_key_usage.key_cert_sign,
"crl_sign": current_key_usage.crl_sign,
"encipher_only": False,
"decipher_only": False,
}
if key_usage["key_agreement"]:
key_usage.update(
dict(
encipher_only=current_key_usage.encipher_only,
decipher_only=current_key_usage.decipher_only,
)
{
"encipher_only": current_key_usage.encipher_only,
"decipher_only": current_key_usage.decipher_only,
}
)
key_usage_names = dict(
digital_signature="Digital Signature",
content_commitment="Non Repudiation",
key_encipherment="Key Encipherment",
data_encipherment="Data Encipherment",
key_agreement="Key Agreement",
key_cert_sign="Certificate Sign",
crl_sign="CRL Sign",
encipher_only="Encipher Only",
decipher_only="Decipher Only",
)
key_usage_names = {
"digital_signature": "Digital Signature",
"content_commitment": "Non Repudiation",
"key_encipherment": "Key Encipherment",
"data_encipherment": "Data Encipherment",
"key_agreement": "Key Agreement",
"key_cert_sign": "Certificate Sign",
"crl_sign": "CRL Sign",
"encipher_only": "Encipher Only",
"decipher_only": "Decipher Only",
}
return (
sorted(
[

View File

@@ -63,7 +63,7 @@ except ImportError:
class OwnCACertificateBackendCryptography(CertificateBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(OwnCACertificateBackendCryptography, self).__init__(module=module)
super().__init__(module=module)
self.create_subject_key_identifier: t.Literal[
"create_if_not_provided", "always_create", "never_create"
@@ -223,7 +223,7 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
not_before: datetime.datetime | None = None,
not_after: datetime.datetime | None = None,
) -> bool:
if super(OwnCACertificateBackendCryptography, self).needs_regeneration(
if super().needs_regeneration(
not_before=self.notBefore, not_after=self.notAfter
):
return True
@@ -272,9 +272,7 @@ class OwnCACertificateBackendCryptography(CertificateBackend):
return False
def dump(self, *, include_certificate: bool) -> dict[str, t.Any]:
result = super(OwnCACertificateBackendCryptography, self).dump(
include_certificate=include_certificate
)
result = super().dump(include_certificate=include_certificate)
result.update(
{
"ca_cert": self.ca_cert_path,
@@ -343,23 +341,23 @@ class OwnCACertificateProvider(CertificateProvider):
def add_ownca_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
argument_spec.argument_spec["provider"]["choices"].append("ownca")
argument_spec.argument_spec.update(
dict(
ownca_path=dict(type="path"),
ownca_content=dict(type="str"),
ownca_privatekey_path=dict(type="path"),
ownca_privatekey_content=dict(type="str", no_log=True),
ownca_privatekey_passphrase=dict(type="str", no_log=True),
ownca_digest=dict(type="str", default="sha256"),
ownca_version=dict(type="int", default=3, choices=[3]), # not used
ownca_not_before=dict(type="str", default="+0s"),
ownca_not_after=dict(type="str", default="+3650d"),
ownca_create_subject_key_identifier=dict(
type="str",
default="create_if_not_provided",
choices=["create_if_not_provided", "always_create", "never_create"],
),
ownca_create_authority_key_identifier=dict(type="bool", default=True),
)
{
"ownca_path": {"type": "path"},
"ownca_content": {"type": "str"},
"ownca_privatekey_path": {"type": "path"},
"ownca_privatekey_content": {"type": "str", "no_log": True},
"ownca_privatekey_passphrase": {"type": "str", "no_log": True},
"ownca_digest": {"type": "str", "default": "sha256"},
"ownca_version": {"type": "int", "default": 3, "choices": [3]}, # not used
"ownca_not_before": {"type": "str", "default": "+0s"},
"ownca_not_after": {"type": "str", "default": "+3650d"},
"ownca_create_subject_key_identifier": {
"type": "str",
"default": "create_if_not_provided",
"choices": ["create_if_not_provided", "always_create", "never_create"],
},
"ownca_create_authority_key_identifier": {"type": "bool", "default": True},
}
)
argument_spec.mutually_exclusive.extend(
[

View File

@@ -59,7 +59,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
privatekey: CertificateIssuerPrivateKeyTypes
def __init__(self, *, module: AnsibleModule) -> None:
super(SelfSignedCertificateBackendCryptography, self).__init__(module=module)
super().__init__(module=module)
self.create_subject_key_identifier: t.Literal[
"create_if_not_provided", "always_create", "never_create"
@@ -144,7 +144,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
critical=False,
)
except ValueError as e:
raise CertificateError(str(e))
raise CertificateError(str(e)) from e
certificate = cert_builder.sign(
private_key=self.privatekey,
@@ -167,7 +167,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
) -> bool:
assert self.privatekey is not None
if super(SelfSignedCertificateBackendCryptography, self).needs_regeneration(
if super().needs_regeneration(
not_before=self.notBefore, not_after=self.notAfter
):
return True
@@ -185,9 +185,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend):
return False
def dump(self, *, include_certificate: bool) -> dict[str, t.Any]:
result = super(SelfSignedCertificateBackendCryptography, self).dump(
include_certificate=include_certificate
)
result = super().dump(include_certificate=include_certificate)
if self.module.check_mode:
result.update(
@@ -243,21 +241,29 @@ class SelfSignedCertificateProvider(CertificateProvider):
def add_selfsigned_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None:
argument_spec.argument_spec["provider"]["choices"].append("selfsigned")
argument_spec.argument_spec.update(
dict(
selfsigned_version=dict(type="int", default=3, choices=[3]), # not used
selfsigned_digest=dict(type="str", default="sha256"),
selfsigned_not_before=dict(
type="str", default="+0s", aliases=["selfsigned_notBefore"]
),
selfsigned_not_after=dict(
type="str", default="+3650d", aliases=["selfsigned_notAfter"]
),
selfsigned_create_subject_key_identifier=dict(
type="str",
default="create_if_not_provided",
choices=["create_if_not_provided", "always_create", "never_create"],
),
)
{
"selfsigned_version": {
"type": "int",
"default": 3,
"choices": [3],
}, # not used
"selfsigned_digest": {"type": "str", "default": "sha256"},
"selfsigned_not_before": {
"type": "str",
"default": "+0s",
"aliases": ["selfsigned_notBefore"],
},
"selfsigned_not_after": {
"type": "str",
"default": "+3650d",
"aliases": ["selfsigned_notAfter"],
},
"selfsigned_create_subject_key_identifier": {
"type": "str",
"default": "create_if_not_provided",
"choices": ["create_if_not_provided", "always_create", "never_create"],
},
}
)

View File

@@ -67,18 +67,18 @@ class CRLInfoRetrieval:
self.name_encoding = module.params.get("name_encoding", "ignore")
def get_info(self) -> dict[str, t.Any]:
self.crl_pem = identify_pem_format(self.content)
crl_pem = identify_pem_format(self.content)
try:
if self.crl_pem:
self.crl = x509.load_pem_x509_crl(self.content)
if crl_pem:
crl = x509.load_pem_x509_crl(self.content)
else:
self.crl = x509.load_der_x509_crl(self.content)
crl = x509.load_der_x509_crl(self.content)
except ValueError as e:
self.module.fail_json(msg=f"Error while decoding CRL: {e}")
result: dict[str, t.Any] = {
"changed": False,
"format": "pem" if self.crl_pem else "der",
"format": "pem" if crl_pem else "der",
"last_update": None,
"next_update": None,
"digest": None,
@@ -86,25 +86,24 @@ class CRLInfoRetrieval:
"issuer": None,
}
result["last_update"] = self.crl.last_update.strftime(TIMESTAMP_FORMAT)
result["last_update"] = crl.last_update.strftime(TIMESTAMP_FORMAT)
result["next_update"] = (
self.crl.next_update.strftime(TIMESTAMP_FORMAT)
if self.crl.next_update
else None
crl.next_update.strftime(TIMESTAMP_FORMAT) if crl.next_update else None
)
result["digest"] = cryptography_oid_to_name(
cryptography_get_signature_algorithm_oid_from_crl(self.crl)
cryptography_get_signature_algorithm_oid_from_crl(crl)
)
issuer = []
for attribute in self.crl.issuer:
for attribute in crl.issuer:
issuer.append([cryptography_oid_to_name(attribute.oid), attribute.value])
result["issuer_ordered"] = issuer
result["issuer"] = {}
issuer_dict = {}
for k, v in issuer:
result["issuer"][k] = v
issuer_dict[k] = v
result["issuer"] = issuer_dict
if self.list_revoked_certificates:
result["revoked_certificates"] = []
for cert in self.crl:
for cert in crl:
entry = cryptography_decode_revoked_certificate(cert)
result["revoked_certificates"].append(
cryptography_dump_revoked(entry, idn_rewrite=self.name_encoding)

View File

@@ -170,7 +170,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
)
self.ordered_subject = True
except ValueError as exc:
raise CertificateSigningRequestError(str(exc))
raise CertificateSigningRequestError(str(exc)) from exc
self.using_common_name_for_san = False
if not self.subjectAltName and module.params["use_common_name_for_san"]:
@@ -189,7 +189,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
except Exception as e:
raise CertificateSigningRequestError(
f"Cannot parse subject_key_identifier: {e}"
)
) from e
self.authority_key_identifier: bytes | None = None
if authority_key_identifier is not None:
@@ -200,7 +200,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
except Exception as e:
raise CertificateSigningRequestError(
f"Cannot parse authority_key_identifier: {e}"
)
) from e
self.existing_csr: cryptography.x509.CertificateSigningRequest | None = None
self.existing_csr_bytes: bytes | None = None
@@ -221,7 +221,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
result["can_parse_csr"] = True
return result
except Exception:
return dict(can_parse_csr=False)
return {"can_parse_csr": False}
@abc.abstractmethod
def generate_csr(self) -> None:
@@ -253,7 +253,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
passphrase=self.privatekey_passphrase,
)
except OpenSSLBadPassphraseError as exc:
raise CertificateSigningRequestError(exc)
raise CertificateSigningRequestError(exc) from exc
@abc.abstractmethod
def _check_csr(self) -> bool:
@@ -294,10 +294,10 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta):
# Store result
result["csr"] = csr_bytes.decode("utf-8") if csr_bytes else None
result["diff"] = dict(
before=self.diff_before,
after=self.diff_after,
)
result["diff"] = {
"before": self.diff_before,
"after": self.diff_after,
}
return result
@@ -347,16 +347,14 @@ def parse_crl_distribution_points(
except (OpenSSLObjectError, ValueError) as e:
raise OpenSSLObjectError(
f"Error while parsing CRL distribution point #{index}: {e}"
)
) from e
return result
# Implementation with using cryptography
class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(CertificateSigningRequestCryptographyBackend, self).__init__(
module=module
)
super().__init__(module=module)
if self.version != 1:
module.warn(
"The cryptography backend only supports version 1. (The only valid value according to RFC 2986.)"
@@ -388,7 +386,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
)
)
except ValueError as e:
raise CertificateSigningRequestError(e)
raise CertificateSigningRequestError(e) from e
if self.subjectAltName:
csr = csr.add_extension(
@@ -451,7 +449,9 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
critical=self.name_constraints_critical,
)
except TypeError as e:
raise OpenSSLObjectError(f"Error while parsing name constraint: {e}")
raise OpenSSLObjectError(
f"Error while parsing name constraint: {e}"
) from e
if self.create_subject_key_identifier:
if not is_potential_certificate_issuer_public_key(
@@ -556,8 +556,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
current_subject = [(sub.oid, sub.value) for sub in csr.subject]
if self.ordered_subject:
return subject == current_subject
else:
return set(subject) == set(current_subject)
return set(subject) == set(current_subject)
def _find_extension(
extensions: cryptography.x509.Extensions, exttype: type[_ET]
@@ -596,15 +595,14 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
)
if not self.keyUsage:
return current_keyusage_ext is None
elif current_keyusage_ext is None:
if current_keyusage_ext is None:
return False
params = cryptography_parse_key_usage_params(self.keyUsage)
for param in params:
if getattr(current_keyusage_ext.value, "_" + param) != params[param]:
for param, value in params.items():
# TODO: check whether getattr() with '_' prepended is really needed
if getattr(current_keyusage_ext.value, "_" + param) != value:
return False
if current_keyusage_ext.critical != self.keyUsage_critical:
return False
return True
return current_keyusage_ext.critical == self.keyUsage_critical
def _check_extenededKeyUsage(extensions: cryptography.x509.Extensions) -> bool:
current_usages_ext = _find_extension(
@@ -647,8 +645,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
bc_ext is not None
and bc_ext.critical == self.basicConstraints_critical
)
else:
return bc_ext is None
return bc_ext is None
def _check_ocspMustStaple(extensions: cryptography.x509.Extensions) -> bool:
tlsfeature_ext = _find_extension(extensions, cryptography.x509.TLSFeature)
@@ -662,8 +659,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
cryptography.x509.TLSFeatureType.status_request
in tlsfeature_ext.value
)
else:
return tlsfeature_ext is None
return tlsfeature_ext is None
def _check_nameConstraints(extensions: cryptography.x509.Extensions) -> bool:
current_nc_ext = _find_extension(
@@ -722,10 +718,8 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
self.privatekey.public_key()
).digest
return ext.value.digest == digest
else:
return ext.value.digest == self.subject_key_identifier
else:
return ext is None
return ext.value.digest == self.subject_key_identifier
return ext is None
def _check_authority_key_identifier(
extensions: cryptography.x509.Extensions,
@@ -753,8 +747,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack
and ext.value.authority_cert_serial_number
== self.authority_cert_serial_number
)
else:
return ext is None
return ext is None
def _check_crl_distribution_points(
extensions: cryptography.x509.Extensions,
@@ -814,77 +807,97 @@ def select_backend(
def get_csr_argument_spec() -> ArgumentSpec:
return ArgumentSpec(
argument_spec=dict(
digest=dict(type="str", default="sha256"),
privatekey_path=dict(type="path"),
privatekey_content=dict(type="str", no_log=True),
privatekey_passphrase=dict(type="str", no_log=True),
version=dict(type="int", default=1, choices=[1]),
subject=dict(type="dict"),
subject_ordered=dict(type="list", elements="dict"),
country_name=dict(type="str", aliases=["C", "countryName"]),
state_or_province_name=dict(
type="str", aliases=["ST", "stateOrProvinceName"]
),
locality_name=dict(type="str", aliases=["L", "localityName"]),
organization_name=dict(type="str", aliases=["O", "organizationName"]),
organizational_unit_name=dict(
type="str", aliases=["OU", "organizationalUnitName"]
),
common_name=dict(type="str", aliases=["CN", "commonName"]),
email_address=dict(type="str", aliases=["E", "emailAddress"]),
subject_alt_name=dict(
type="list", elements="str", aliases=["subjectAltName"]
),
subject_alt_name_critical=dict(
type="bool", default=False, aliases=["subjectAltName_critical"]
),
use_common_name_for_san=dict(
type="bool", default=True, aliases=["useCommonNameForSAN"]
),
key_usage=dict(type="list", elements="str", aliases=["keyUsage"]),
key_usage_critical=dict(
type="bool", default=False, aliases=["keyUsage_critical"]
),
extended_key_usage=dict(
type="list", elements="str", aliases=["extKeyUsage", "extendedKeyUsage"]
),
extended_key_usage_critical=dict(
type="bool",
default=False,
aliases=["extKeyUsage_critical", "extendedKeyUsage_critical"],
),
basic_constraints=dict(
type="list", elements="str", aliases=["basicConstraints"]
),
basic_constraints_critical=dict(
type="bool", default=False, aliases=["basicConstraints_critical"]
),
ocsp_must_staple=dict(
type="bool", default=False, aliases=["ocspMustStaple"]
),
ocsp_must_staple_critical=dict(
type="bool", default=False, aliases=["ocspMustStaple_critical"]
),
name_constraints_permitted=dict(type="list", elements="str"),
name_constraints_excluded=dict(type="list", elements="str"),
name_constraints_critical=dict(type="bool", default=False),
create_subject_key_identifier=dict(type="bool", default=False),
subject_key_identifier=dict(type="str"),
authority_key_identifier=dict(type="str"),
authority_cert_issuer=dict(type="list", elements="str"),
authority_cert_serial_number=dict(type="int"),
crl_distribution_points=dict(
type="list",
elements="dict",
options=dict(
full_name=dict(type="list", elements="str"),
relative_name=dict(type="list", elements="str"),
crl_issuer=dict(type="list", elements="str"),
reasons=dict(
type="list",
elements="str",
choices=[
argument_spec={
"digest": {"type": "str", "default": "sha256"},
"privatekey_path": {"type": "path"},
"privatekey_content": {"type": "str", "no_log": True},
"privatekey_passphrase": {"type": "str", "no_log": True},
"version": {"type": "int", "default": 1, "choices": [1]},
"subject": {"type": "dict"},
"subject_ordered": {"type": "list", "elements": "dict"},
"country_name": {"type": "str", "aliases": ["C", "countryName"]},
"state_or_province_name": {
"type": "str",
"aliases": ["ST", "stateOrProvinceName"],
},
"locality_name": {"type": "str", "aliases": ["L", "localityName"]},
"organization_name": {"type": "str", "aliases": ["O", "organizationName"]},
"organizational_unit_name": {
"type": "str",
"aliases": ["OU", "organizationalUnitName"],
},
"common_name": {"type": "str", "aliases": ["CN", "commonName"]},
"email_address": {"type": "str", "aliases": ["E", "emailAddress"]},
"subject_alt_name": {
"type": "list",
"elements": "str",
"aliases": ["subjectAltName"],
},
"subject_alt_name_critical": {
"type": "bool",
"default": False,
"aliases": ["subjectAltName_critical"],
},
"use_common_name_for_san": {
"type": "bool",
"default": True,
"aliases": ["useCommonNameForSAN"],
},
"key_usage": {"type": "list", "elements": "str", "aliases": ["keyUsage"]},
"key_usage_critical": {
"type": "bool",
"default": False,
"aliases": ["keyUsage_critical"],
},
"extended_key_usage": {
"type": "list",
"elements": "str",
"aliases": ["extKeyUsage", "extendedKeyUsage"],
},
"extended_key_usage_critical": {
"type": "bool",
"default": False,
"aliases": ["extKeyUsage_critical", "extendedKeyUsage_critical"],
},
"basic_constraints": {
"type": "list",
"elements": "str",
"aliases": ["basicConstraints"],
},
"basic_constraints_critical": {
"type": "bool",
"default": False,
"aliases": ["basicConstraints_critical"],
},
"ocsp_must_staple": {
"type": "bool",
"default": False,
"aliases": ["ocspMustStaple"],
},
"ocsp_must_staple_critical": {
"type": "bool",
"default": False,
"aliases": ["ocspMustStaple_critical"],
},
"name_constraints_permitted": {"type": "list", "elements": "str"},
"name_constraints_excluded": {"type": "list", "elements": "str"},
"name_constraints_critical": {"type": "bool", "default": False},
"create_subject_key_identifier": {"type": "bool", "default": False},
"subject_key_identifier": {"type": "str"},
"authority_key_identifier": {"type": "str"},
"authority_cert_issuer": {"type": "list", "elements": "str"},
"authority_cert_serial_number": {"type": "int"},
"crl_distribution_points": {
"type": "list",
"elements": "dict",
"options": {
"full_name": {"type": "list", "elements": "str"},
"relative_name": {"type": "list", "elements": "str"},
"crl_issuer": {"type": "list", "elements": "str"},
"reasons": {
"type": "list",
"elements": "str",
"choices": [
"key_compromise",
"ca_compromise",
"affiliation_changed",
@@ -894,15 +907,17 @@ def get_csr_argument_spec() -> ArgumentSpec:
"privilege_withdrawn",
"aa_compromise",
],
),
),
mutually_exclusive=[("full_name", "relative_name")],
required_one_of=[("full_name", "relative_name", "crl_issuer")],
),
select_crypto_backend=dict(
type="str", default="auto", choices=["auto", "cryptography"]
),
),
},
},
"mutually_exclusive": [("full_name", "relative_name")],
"required_one_of": [("full_name", "relative_name", "crl_issuer")],
},
"select_crypto_backend": {
"type": "str",
"default": "auto",
"choices": ["auto", "cryptography"],
},
},
required_together=[
["authority_cert_issuer", "authority_cert_serial_number"],
],

View File

@@ -61,6 +61,8 @@ TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
class CSRInfoRetrieval(metaclass=abc.ABCMeta):
csr: x509.CertificateSigningRequest
def __init__(
self, *, module: GeneralAnsibleModule, content: bytes, validate_signature: bool
) -> None:
@@ -129,7 +131,7 @@ class CSRInfoRetrieval(metaclass=abc.ABCMeta):
)
subject = self._get_subject_ordered()
result["subject"] = dict()
result["subject"] = {}
for k, v in subject:
result["subject"][k] = v
result["subject_ordered"] = subject
@@ -197,7 +199,7 @@ class CSRInfoRetrievalCryptography(CSRInfoRetrieval):
def __init__(
self, *, module: GeneralAnsibleModule, content: bytes, validate_signature: bool
) -> None:
super(CSRInfoRetrievalCryptography, self).__init__(
super().__init__(
module=module, content=content, validate_signature=validate_signature
)
self.name_encoding: t.Literal["ignore", "idna", "unicode"] = module.params.get(
@@ -216,36 +218,36 @@ class CSRInfoRetrievalCryptography(CSRInfoRetrieval):
try:
current_key_ext = self.csr.extensions.get_extension_for_class(x509.KeyUsage)
current_key_usage = current_key_ext.value
key_usage = dict(
digital_signature=current_key_usage.digital_signature,
content_commitment=current_key_usage.content_commitment,
key_encipherment=current_key_usage.key_encipherment,
data_encipherment=current_key_usage.data_encipherment,
key_agreement=current_key_usage.key_agreement,
key_cert_sign=current_key_usage.key_cert_sign,
crl_sign=current_key_usage.crl_sign,
encipher_only=False,
decipher_only=False,
)
key_usage = {
"digital_signature": current_key_usage.digital_signature,
"content_commitment": current_key_usage.content_commitment,
"key_encipherment": current_key_usage.key_encipherment,
"data_encipherment": current_key_usage.data_encipherment,
"key_agreement": current_key_usage.key_agreement,
"key_cert_sign": current_key_usage.key_cert_sign,
"crl_sign": current_key_usage.crl_sign,
"encipher_only": False,
"decipher_only": False,
}
if key_usage["key_agreement"]:
key_usage.update(
dict(
encipher_only=current_key_usage.encipher_only,
decipher_only=current_key_usage.decipher_only,
)
{
"encipher_only": current_key_usage.encipher_only,
"decipher_only": current_key_usage.decipher_only,
}
)
key_usage_names = dict(
digital_signature="Digital Signature",
content_commitment="Non Repudiation",
key_encipherment="Key Encipherment",
data_encipherment="Data Encipherment",
key_agreement="Key Agreement",
key_cert_sign="Certificate Sign",
crl_sign="CRL Sign",
encipher_only="Encipher Only",
decipher_only="Decipher Only",
)
key_usage_names = {
"digital_signature": "Digital Signature",
"content_commitment": "Non Repudiation",
"key_encipherment": "Key Encipherment",
"data_encipherment": "Data Encipherment",
"key_agreement": "Key Agreement",
"key_cert_sign": "Certificate Sign",
"crl_sign": "CRL Sign",
"encipher_only": "Encipher Only",
"decipher_only": "Decipher Only",
}
return (
sorted(
[

View File

@@ -265,10 +265,10 @@ class PrivateKeyBackend(metaclass=abc.ABCMeta):
else:
result["privatekey"] = None
result["diff"] = dict(
before=self.diff_before,
after=self.diff_after,
)
result["diff"] = {
"before": self.diff_before,
"after": self.diff_after,
}
return result
@@ -323,7 +323,7 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
self.curves[name] = _Curve(name=name, ectype=ectype, deprecated=deprecated)
def __init__(self, module: GeneralAnsibleModule) -> None:
super(PrivateKeyCryptographyBackend, self).__init__(module=module)
super().__init__(module=module)
self.curves: dict[str, _Curve] = {}
self._add_curve("secp224r1", "SECP224R1")
@@ -351,8 +351,7 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
return self.format # type: ignore
if self.type in ("X25519", "X448", "Ed25519", "Ed448"):
return "pkcs8"
else:
return "pkcs1"
return "pkcs1"
def generate_private_key(self) -> None:
"""(Re-)Generate private key."""
@@ -427,6 +426,9 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
export_encoding = (
cryptography.hazmat.primitives.serialization.Encoding.Raw
)
else:
# pylint does not notice that all possible values for export_format_txt have been covered.
raise AssertionError("Can never be reached") # pragma: no cover
except AttributeError:
self.module.fail_json(
msg=f'Cryptography backend does not support the selected output format "{self.format}"'
@@ -469,8 +471,8 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
raise AssertionError("existing_private_key_bytes not set")
try:
# Interpret bytes depending on format.
format = identify_private_key_format(data)
if format == "raw":
key_format = identify_private_key_format(data)
if key_format == "raw":
if len(data) == 56:
return cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.from_private_bytes(
data
@@ -497,15 +499,13 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
data
)
raise PrivateKeyError("Cannot load raw key")
else:
return (
cryptography.hazmat.primitives.serialization.load_pem_private_key(
data,
None if self.passphrase is None else to_bytes(self.passphrase),
)
)
return cryptography.hazmat.primitives.serialization.load_pem_private_key(
data,
None if self.passphrase is None else to_bytes(self.passphrase),
)
except Exception as e:
raise PrivateKeyError(e)
raise PrivateKeyError(e) from e
def _ensure_existing_private_key_loaded(self) -> None:
if self.existing_private_key is None and self.has_existing():
@@ -515,21 +515,20 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
if self.existing_private_key_bytes is None:
raise AssertionError("existing_private_key_bytes not set")
try:
format = identify_private_key_format(self.existing_private_key_bytes)
if format == "raw":
key_format = identify_private_key_format(self.existing_private_key_bytes)
if key_format == "raw":
# Raw keys cannot be encrypted. To avoid incompatibilities, we try to
# actually load the key (and return False when this fails).
self._load_privatekey()
# Loading the key succeeded. Only return True when no passphrase was
# provided.
return self.passphrase is None
else:
return bool(
cryptography.hazmat.primitives.serialization.load_pem_private_key(
self.existing_private_key_bytes,
None if self.passphrase is None else to_bytes(self.passphrase),
)
return bool(
cryptography.hazmat.primitives.serialization.load_pem_private_key(
self.existing_private_key_bytes,
None if self.passphrase is None else to_bytes(self.passphrase),
)
)
except Exception:
return False
@@ -588,8 +587,8 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend):
if self.format == "auto_ignore":
return True
try:
format = identify_private_key_format(self.existing_private_key_bytes)
return format == self._get_wanted_format()
key_format = identify_private_key_format(self.existing_private_key_bytes)
return key_format == self._get_wanted_format()
except Exception:
return False
@@ -603,16 +602,16 @@ def select_backend(module: GeneralAnsibleModule) -> PrivateKeyBackend:
def get_privatekey_argument_spec() -> ArgumentSpec:
return ArgumentSpec(
argument_spec=dict(
size=dict(type="int", default=4096),
type=dict(
type="str",
default="RSA",
choices=["DSA", "ECC", "Ed25519", "Ed448", "RSA", "X25519", "X448"],
),
curve=dict(
type="str",
choices=[
argument_spec={
"size": {"type": "int", "default": 4096},
"type": {
"type": "str",
"default": "RSA",
"choices": ["DSA", "ECC", "Ed25519", "Ed448", "RSA", "X25519", "X448"],
},
"curve": {
"type": "str",
"choices": [
"secp224r1",
"secp256k1",
"secp256r1",
@@ -633,32 +632,36 @@ def get_privatekey_argument_spec() -> ArgumentSpec:
"sect571k1",
"sect571r1",
],
),
passphrase=dict(type="str", no_log=True),
cipher=dict(type="str", default="auto"),
format=dict(
type="str",
default="auto_ignore",
choices=["pkcs1", "pkcs8", "raw", "auto", "auto_ignore"],
),
format_mismatch=dict(
type="str", default="regenerate", choices=["regenerate", "convert"]
),
select_crypto_backend=dict(
type="str", choices=["auto", "cryptography"], default="auto"
),
regenerate=dict(
type="str",
default="full_idempotence",
choices=[
},
"passphrase": {"type": "str", "no_log": True},
"cipher": {"type": "str", "default": "auto"},
"format": {
"type": "str",
"default": "auto_ignore",
"choices": ["pkcs1", "pkcs8", "raw", "auto", "auto_ignore"],
},
"format_mismatch": {
"type": "str",
"default": "regenerate",
"choices": ["regenerate", "convert"],
},
"select_crypto_backend": {
"type": "str",
"choices": ["auto", "cryptography"],
"default": "auto",
},
"regenerate": {
"type": "str",
"default": "full_idempotence",
"choices": [
"never",
"fail",
"partial_idempotence",
"full_idempotence",
"always",
],
),
),
},
},
required_if=[
("type", "ECC", ["curve"]),
],

View File

@@ -121,7 +121,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta):
assert self.dest_private_key_bytes is not None
try:
format, self.dest_private_key = self._load_private_key(
key_format, self.dest_private_key = self._load_private_key(
data=self.dest_private_key_bytes,
passphrase=self.dest_passphrase,
current_hint=self.src_private_key,
@@ -129,7 +129,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta):
except Exception:
return True
return format != self.format or not cryptography_compare_private_keys(
return key_format != self.format or not cryptography_compare_private_keys(
self.dest_private_key, self.src_private_key
)
@@ -141,7 +141,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta):
# Implementation with using cryptography
class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(PrivateKeyConvertCryptographyBackend, self).__init__(module=module)
super().__init__(module=module)
def get_private_key_data(self) -> bytes:
"""Return bytes for self.src_private_key in output format"""
@@ -166,6 +166,9 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
export_encoding = (
cryptography.hazmat.primitives.serialization.Encoding.Raw
)
else:
# pylint does not notice that all possible values for self.format have been covered.
raise AssertionError("Can never be reached") # pragma: no cover
except AttributeError:
self.module.fail_json(
msg=f'Cryptography backend does not support the selected output format "{self.format}"'
@@ -208,20 +211,20 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
) -> tuple[str, PrivateKeyTypes]:
try:
# Interpret bytes depending on format.
format = identify_private_key_format(data)
if format == "raw":
key_format = identify_private_key_format(data)
if key_format == "raw":
if passphrase is not None:
raise PrivateKeyError("Cannot load raw key with passphrase")
if len(data) == 56:
return (
format,
key_format,
cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.from_private_bytes(
data
),
)
if len(data) == 57:
return (
format,
key_format,
cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey.from_private_bytes(
data
),
@@ -233,14 +236,14 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
):
try:
return (
format,
key_format,
cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes(
data
),
)
except Exception:
return (
format,
key_format,
cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes(
data
),
@@ -248,29 +251,29 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend):
else:
try:
return (
format,
key_format,
cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes(
data
),
)
except Exception:
return (
format,
key_format,
cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes(
data
),
)
raise PrivateKeyError("Cannot load raw key")
else:
return (
format,
cryptography.hazmat.primitives.serialization.load_pem_private_key(
data,
None if passphrase is None else to_bytes(passphrase),
),
)
return (
key_format,
cryptography.hazmat.primitives.serialization.load_pem_private_key(
data,
None if passphrase is None else to_bytes(passphrase),
),
)
except Exception as e:
raise PrivateKeyError(e)
raise PrivateKeyError(e) from e
def select_backend(module: AnsibleModule) -> PrivateKeyConvertBackend:
@@ -282,13 +285,17 @@ def select_backend(module: AnsibleModule) -> PrivateKeyConvertBackend:
def get_privatekey_argument_spec() -> ArgumentSpec:
return ArgumentSpec(
argument_spec=dict(
src_path=dict(type="path"),
src_content=dict(type="str"),
src_passphrase=dict(type="str", no_log=True),
dest_passphrase=dict(type="str", no_log=True),
format=dict(type="str", required=True, choices=["pkcs1", "pkcs8", "raw"]),
),
argument_spec={
"src_path": {"type": "path"},
"src_content": {"type": "str"},
"src_passphrase": {"type": "str", "no_log": True},
"dest_passphrase": {"type": "str", "no_log": True},
"format": {
"type": "str",
"required": True,
"choices": ["pkcs1", "pkcs8", "raw"],
},
},
mutually_exclusive=[
["src_path", "src_content"],
],

View File

@@ -134,7 +134,7 @@ def _is_cryptography_key_consistent(
# key._backend was removed in cryptography 42.0.0
backend = getattr(key, "_backend", None)
if backend is not None:
return bool(backend._lib.RSA_check_key(key._rsa_cdata)) # type: ignore
return bool(backend._lib.RSA_check_key(key._rsa_cdata)) # type: ignore # pylint: disable=protected-access
if isinstance(key, cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey):
result = _check_dsa_consistency(
key_public_data=key_public_data, key_private_data=key_private_data
@@ -195,19 +195,21 @@ def _is_cryptography_key_consistent(
class PrivateKeyConsistencyError(OpenSSLObjectError):
def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None:
super(PrivateKeyConsistencyError, self).__init__(msg)
super().__init__(msg)
self.error_message = msg
self.result = result
class PrivateKeyParseError(OpenSSLObjectError):
def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None:
super(PrivateKeyParseError, self).__init__(msg)
super().__init__(msg)
self.error_message = msg
self.result = result
class PrivateKeyInfoRetrieval(metaclass=abc.ABCMeta):
key: PrivateKeyTypes
def __init__(
self,
*,
@@ -257,14 +259,14 @@ class PrivateKeyInfoRetrieval(metaclass=abc.ABCMeta):
)
result["can_parse_key"] = True
except OpenSSLObjectError as exc:
raise PrivateKeyParseError(str(exc), result=result)
raise PrivateKeyParseError(str(exc), result=result) from exc
result["public_key"] = to_native(self._get_public_key(binary=False))
pk = self._get_public_key(binary=True)
result["public_key_fingerprints"] = (
get_fingerprint_of_bytes(pk, prefer_one=prefer_one_fingerprint)
if pk is not None
else dict()
else {}
)
key_type, key_public_data, key_private_data = self._get_key_info(
@@ -295,9 +297,7 @@ class PrivateKeyInfoRetrievalCryptography(PrivateKeyInfoRetrieval):
def __init__(
self, *, module: GeneralAnsibleModule, content: bytes, **kwargs
) -> None:
super(PrivateKeyInfoRetrievalCryptography, self).__init__(
module=module, content=content, **kwargs
)
super().__init__(module=module, content=content, **kwargs)
def _get_public_key(self, *, binary: bool) -> bytes:
return self.key.public_key().public_bytes(

View File

@@ -100,7 +100,7 @@ def _get_cryptography_public_key_info(
class PublicKeyParseError(OpenSSLObjectError):
def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None:
super(PublicKeyParseError, self).__init__(msg)
super().__init__(msg)
self.error_message = msg
self.result = result
@@ -132,13 +132,13 @@ class PublicKeyInfoRetrieval(metaclass=abc.ABCMeta):
try:
self.key = load_publickey(content=self.content)
except OpenSSLObjectError as e:
raise PublicKeyParseError(str(e), result={})
raise PublicKeyParseError(str(e), result={}) from e
pk = self._get_public_key(binary=True)
result["fingerprints"] = (
get_fingerprint_of_bytes(pk, prefer_one=prefer_one_fingerprint)
if pk is not None
else dict()
else {}
)
key_type, key_public_data = self._get_key_info()
@@ -157,9 +157,7 @@ class PublicKeyInfoRetrievalCryptography(PublicKeyInfoRetrieval):
content: bytes | None = None,
key: PublicKeyTypes | None = None,
) -> None:
super(PublicKeyInfoRetrievalCryptography, self).__init__(
module=module, content=content, key=key
)
super().__init__(module=module, content=content, key=key)
def _get_public_key(self, binary: bool) -> bytes:
if self.key is None:

View File

@@ -161,19 +161,21 @@ def load_privatekey(
else:
priv_key_detail = content
except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc)
raise OpenSSLObjectError(exc) from exc
try:
return load_pem_private_key(
priv_key_detail,
None if passphrase is None else to_bytes(passphrase),
)
except TypeError:
except TypeError as exc:
raise OpenSSLBadPassphraseError(
"Wrong or empty passphrase provided for private key"
)
except ValueError:
raise OpenSSLBadPassphraseError("Wrong passphrase provided for private key")
) from exc
except ValueError as exc:
raise OpenSSLBadPassphraseError(
"Wrong passphrase provided for private key"
) from exc
def load_certificate_privatekey(
@@ -232,12 +234,12 @@ def load_publickey(
with open(path, "rb") as b_priv_key_fh:
content = b_priv_key_fh.read()
except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc)
raise OpenSSLObjectError(exc) from exc
try:
return serialization.load_pem_public_key(content)
except Exception as e:
raise OpenSSLObjectError(f"Error while deserializing key: {e}")
raise OpenSSLObjectError(f"Error while deserializing key: {e}") from e
def load_certificate(
@@ -257,17 +259,17 @@ def load_certificate(
else:
cert_content = content
except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc)
raise OpenSSLObjectError(exc) from exc
if der_support_enabled is False or identify_pem_format(cert_content):
try:
return x509.load_pem_x509_certificate(cert_content)
except ValueError as exc:
raise OpenSSLObjectError(exc)
raise OpenSSLObjectError(exc) from exc
elif der_support_enabled:
try:
return x509.load_der_x509_certificate(cert_content)
except ValueError as exc:
raise OpenSSLObjectError(f"Cannot parse DER certificate: {exc}")
raise OpenSSLObjectError(f"Cannot parse DER certificate: {exc}") from exc
def load_certificate_request(
@@ -283,11 +285,11 @@ def load_certificate_request(
else:
csr_content = content
except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc)
raise OpenSSLObjectError(exc) from exc
try:
return x509.load_pem_x509_csr(csr_content)
except ValueError as exc:
raise OpenSSLObjectError(exc)
raise OpenSSLObjectError(exc) from exc
def parse_name_field(
@@ -344,7 +346,7 @@ def parse_ordered_name_field(
except (TypeError, ValueError) as exc:
raise ValueError(
f"Error while processing entry #{index + 1} in {name_field_name}: {exc}"
)
) from exc
return result
@@ -425,9 +427,7 @@ class OpenSSLObject(metaclass=abc.ABCMeta):
self.changed = True
except OSError as exc:
if exc.errno != errno.ENOENT:
raise OpenSSLObjectError(exc)
else:
pass
raise OpenSSLObjectError(exc) from exc
__all__ = (