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

@@ -119,7 +119,7 @@ class ACMEAccount:
if "location" in info:
self.client.set_account_uri(info["location"])
return True, result
elif info["status"] == 200:
if info["status"] == 200:
# Account did exist
if result.get("status") == "deactivated":
# A bug in Pebble (https://github.com/letsencrypt/pebble/issues/179) and
@@ -130,12 +130,11 @@ class ACMEAccount:
# requests authorized by that account's key."
if not allow_creation:
return False, None
else:
raise ModuleFailException("Account is deactivated")
raise ModuleFailException("Account is deactivated")
if "location" in info:
self.client.set_account_uri(info["location"])
return False, result
elif (
if (
info["status"] in (400, 404)
and result["type"] == "urn:ietf:params:acme:error:accountDoesNotExist"
and not allow_creation
@@ -144,7 +143,7 @@ class ACMEAccount:
# (According to RFC 8555, Section 7.3.1, the HTTP status code MUST be 400.
# Unfortunately Digicert does not care and sends 404 instead.)
return False, None
elif (
if (
info["status"] == 403
and result["type"] == "urn:ietf:params:acme:error:unauthorized"
and "deactivated" in (result.get("detail") or "")
@@ -154,15 +153,13 @@ class ACMEAccount:
# might need adjustment in error detection.
if not allow_creation:
return False, None
else:
raise ModuleFailException("Account is deactivated")
else:
raise ACMEProtocolException(
module=self.client.module,
msg="Registering ACME account failed",
info=info,
content_json=result,
)
raise ModuleFailException("Account is deactivated")
raise ACMEProtocolException(
module=self.client.module,
msg="Registering ACME account failed",
info=info,
content_json=result,
)
def get_account_data(self) -> dict[str, t.Any] | None:
"""

View File

@@ -244,7 +244,9 @@ class ACMEClient:
passphrase=self.account_key_passphrase,
)
except KeyParsingError as e:
raise ModuleFailException(f"Error while parsing account key: {e.msg}")
raise ModuleFailException(
f"Error while parsing account key: {e.msg}"
) from e
self.account_jwk = self.account_key_data["jwk"]
self.account_jws_header = {
"alg": self.account_key_data["alg"],
@@ -307,7 +309,7 @@ class ACMEClient:
except Exception as e:
raise ModuleFailException(
f"Failed to encode payload / headers as JSON: {e}"
)
) from e
return self.backend.sign(
payload64=payload64, protected64=protected64, key_data=key_data
@@ -456,10 +458,10 @@ class ACMEClient:
result = decoded_result
else:
result = content
except ValueError:
except ValueError as exc:
raise NetworkException(
f"Failed to parse the ACME response: {url} {content}"
)
) from exc
else:
result = content
@@ -569,10 +571,10 @@ class ACMEClient:
try:
result = self.module.from_json(content.decode("utf8"))
parsed_json_result = True
except ValueError:
except ValueError as exc:
raise NetworkException(
f"Failed to parse the ACME response: {uri} {content!r}"
)
) from exc
else:
result = content
else:
@@ -642,30 +644,32 @@ def create_default_argspec(
Provides default argument spec for the options documented in the acme doc fragment.
"""
result = ArgumentSpec(
argument_spec=dict(
acme_directory=dict(type="str", required=True),
acme_version=dict(type="int", choices=[2], default=2),
validate_certs=dict(type="bool", default=True),
select_crypto_backend=dict(
type="str", default="auto", choices=["auto", "openssl", "cryptography"]
),
request_timeout=dict(type="int", default=10),
),
argument_spec={
"acme_directory": {"type": "str", "required": True},
"acme_version": {"type": "int", "choices": [2], "default": 2},
"validate_certs": {"type": "bool", "default": True},
"select_crypto_backend": {
"type": "str",
"default": "auto",
"choices": ["auto", "openssl", "cryptography"],
},
"request_timeout": {"type": "int", "default": 10},
},
)
if with_account:
result.update_argspec(
account_key_src=dict(type="path", aliases=["account_key"]),
account_key_content=dict(type="str", no_log=True),
account_key_passphrase=dict(type="str", no_log=True),
account_uri=dict(type="str"),
account_key_src={"type": "path", "aliases": ["account_key"]},
account_key_content={"type": "str", "no_log": True},
account_key_passphrase={"type": "str", "no_log": True},
account_uri={"type": "str"},
)
if require_account_key:
result.update(required_one_of=[["account_key_src", "account_key_content"]])
result.update(mutually_exclusive=[["account_key_src", "account_key_content"]])
if with_certificate:
result.update_argspec(
csr=dict(type="path"),
csr_content=dict(type="str"),
csr={"type": "path"},
csr_content={"type": "str"},
)
result.update(
required_one_of=[["csr", "csr_content"]],

View File

@@ -211,9 +211,7 @@ class CryptographyChainMatcher(ChainMatcher):
class CryptographyBackend(CryptoBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(CryptographyBackend, self).__init__(
module=module, with_timezone=CRYPTOGRAPHY_TIMEZONE
)
super().__init__(module=module, with_timezone=CRYPTOGRAPHY_TIMEZONE)
def parse_key(
self,
@@ -242,7 +240,7 @@ class CryptographyBackend(CryptoBackend):
password=to_bytes(passphrase) if passphrase is not None else None,
)
except Exception as e:
raise KeyParsingError(f"error while loading key: {e}")
raise KeyParsingError(f"error while loading key: {e}") from e
if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
rsa_pk = key.public_key().public_numbers()
return {
@@ -256,7 +254,7 @@ class CryptographyBackend(CryptoBackend):
},
"hash": "sha256",
}
elif isinstance(
if isinstance(
key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey
):
ec_pk = key.public_key().public_numbers()
@@ -296,8 +294,7 @@ class CryptographyBackend(CryptoBackend):
"hash": hashalg,
"point_size": point_size,
}
else:
raise KeyParsingError(f'unknown key type "{type(key)}"')
raise KeyParsingError(f'unknown key type "{type(key)}"')
def sign(
self, *, payload64: str, protected64: str, key_data: dict[str, t.Any]
@@ -332,6 +329,8 @@ class CryptographyBackend(CryptoBackend):
rr = convert_int_to_hex(r, digits=2 * key_data["point_size"])
ss = convert_int_to_hex(s, digits=2 * key_data["point_size"])
signature = binascii.unhexlify(rr) + binascii.unhexlify(ss)
else:
raise AssertionError("Can never be reached") # pragma: no cover
return {
"protected": protected64,
@@ -472,8 +471,10 @@ class CryptographyBackend(CryptoBackend):
cert = cryptography.x509.load_pem_x509_certificate(b_cert_content)
except Exception as e:
if cert_filename is None:
raise BackendException(f"Cannot parse certificate: {e}")
raise BackendException(f"Cannot parse certificate {cert_filename}: {e}")
raise BackendException(f"Cannot parse certificate: {e}") from e
raise BackendException(
f"Cannot parse certificate {cert_filename}: {e}"
) from e
if now is None:
now = self.get_now()
@@ -508,8 +509,10 @@ class CryptographyBackend(CryptoBackend):
cert = cryptography.x509.load_pem_x509_certificate(b_cert_content)
except Exception as e:
if cert_filename is None:
raise BackendException(f"Cannot parse certificate: {e}")
raise BackendException(f"Cannot parse certificate {cert_filename}: {e}")
raise BackendException(f"Cannot parse certificate: {e}") from e
raise BackendException(
f"Cannot parse certificate {cert_filename}: {e}"
) from e
ski = None
try:

View File

@@ -45,7 +45,12 @@ if t.TYPE_CHECKING:
)
_OPENSSL_ENVIRONMENT_UPDATE = dict(LANG="C", LC_ALL="C", LC_MESSAGES="C", LC_CTYPE="C")
_OPENSSL_ENVIRONMENT_UPDATE = {
"LANG": "C",
"LC_ALL": "C",
"LC_MESSAGES": "C",
"LC_CTYPE": "C",
}
def _extract_date(
@@ -66,7 +71,7 @@ def _extract_date(
except ValueError as exc:
raise BackendException(
f"Failed to parse '{name}' date{cert_filename_suffix}: {exc}"
)
) from exc
def _decode_octets(octets_text: str) -> bytes:
@@ -118,7 +123,7 @@ class OpenSSLCLIBackend(CryptoBackend):
def __init__(
self, *, module: AnsibleModule, openssl_binary: str | None = None
) -> None:
super(OpenSSLCLIBackend, self).__init__(module=module, with_timezone=True)
super().__init__(module=module, with_timezone=True)
if openssl_binary is None:
openssl_binary = module.get_bin_path("openssl", True)
self.openssl_binary = openssl_binary
@@ -156,11 +161,11 @@ class OpenSSLCLIBackend(CryptoBackend):
raise KeyParsingError(
f"failed to create temporary content file: {err}",
exception=traceback.format_exc(),
)
) from err
f.close()
# Parse key
account_key_type = None
with open(key_file, "rt") as fi:
with open(key_file, "r", encoding="utf-8") as fi:
for line in fi:
m = re.match(
r"^\s*-{5,}BEGIN\s+(EC|RSA)\s+PRIVATE\s+KEY-{5,}\s*$", line
@@ -228,7 +233,7 @@ class OpenSSLCLIBackend(CryptoBackend):
},
"hash": "sha256",
}
elif account_key_type == "ec":
if account_key_type == "ec":
pub_data = re.search(
r"pub:\s*\n\s+04:([a-f0-9\:\s]+?)\nASN1 OID: (\S+)(?:\nNIST CURVE: (\S+))?",
out_text,

View File

@@ -77,14 +77,14 @@ def _parse_acme_timestamp(
"""
# RFC 3339 (https://www.rfc-editor.org/info/rfc3339)
timestamp_str = _reduce_fractional_digits(timestamp_str)
for format in (
for time_format in (
"%Y-%m-%dT%H:%M:%SZ",
"%Y-%m-%dT%H:%M:%S.%fZ",
"%Y-%m-%dT%H:%M:%S%z",
"%Y-%m-%dT%H:%M:%S.%f%z",
):
try:
result = datetime.datetime.strptime(timestamp_str, format)
result = datetime.datetime.strptime(timestamp_str, time_format)
except ValueError:
pass
else:
@@ -117,7 +117,7 @@ class CryptoBackend(metaclass=abc.ABCMeta):
raise BackendException(f"Invalid value for {name}: {value!r}")
return result
except OpenSSLObjectError as exc:
raise BackendException(str(exc))
raise BackendException(str(exc)) from exc
def interpolate_timestamp(
self,

View File

@@ -166,11 +166,11 @@ class ACMECertificateClient:
continue
challenge_data = authz.get_challenge_data(client=self.client)
data.append(
dict(
identifier=authz.identifier,
identifier_type=authz.identifier_type,
challenges=challenge_data,
)
{
"identifier": authz.identifier,
"identifier_type": authz.identifier_type,
"challenges": challenge_data,
}
)
dns_challenge = challenge_data.get(dns_challenge_type)
if dns_challenge:
@@ -321,7 +321,7 @@ class ACMECertificateClient:
"""
if self.csr is None and self.csr_content is None:
raise ModuleFailException("No CSR has been provided")
for identifier, authz in order.authorizations.items():
for authz in order.authorizations.values():
if authz.status != "valid":
authz.raise_error(
error_msg=f'Status is {authz.status!r} and not "valid"',

View File

@@ -71,7 +71,7 @@ class CertificateChain:
process_links(
info=info,
callback=lambda link, relation: result._process_links(
callback=lambda link, relation: result._process_links( # pylint: disable=protected-access
client=client, link=link, relation=relation
),
)

View File

@@ -295,10 +295,10 @@ class Authorization:
raise ACMEProtocolException(
module=module,
msg=f"Failed to validate challenge for {self.combined_identifier}: {error_msg}. {'; '.join(error_details)}",
extras=dict(
identifier=self.combined_identifier,
authorization=self.data,
),
extras={
"identifier": self.combined_identifier,
"authorization": self.data,
},
)
def find_challenge(self, *, challenge_type: str) -> Challenge | None:
@@ -374,7 +374,7 @@ class Authorization:
"""
authz = cls(url=url)
authz_deactivate = {"status": "deactivated"}
result, info = client.send_signed_request(
result, _info = client.send_signed_request(
url, authz_deactivate, fail_on_error=True
)
authz._setup(client=client, data=result)

View File

@@ -40,12 +40,12 @@ def format_error_problem(
subproblems = problem.get("subproblems")
if subproblems is not None:
msg = f"{msg} Subproblems:"
for index, problem in enumerate(subproblems):
for index, subproblem in enumerate(subproblems):
index_str = f"{subproblem_prefix}{index}"
problem_str = format_error_problem(
problem, subproblem_prefix=f"{index_str}."
subproblem_str = format_error_problem(
subproblem, subproblem_prefix=f"{index_str}."
)
msg = f"{msg}\n({index_str}) {problem_str}"
msg = f"{msg}\n({index_str}) {subproblem_str}"
return msg
@@ -55,7 +55,7 @@ class ModuleFailException(Exception):
"""
def __init__(self, msg: str, **args: t.Any) -> None:
super(ModuleFailException, self).__init__(self, msg)
super().__init__(self, msg)
self.msg = msg
self.module_fail_args = args
@@ -100,7 +100,7 @@ class ACMEProtocolException(ModuleFailException):
except Exception:
pass
extras = extras or dict()
extras = extras or {}
error_code = None
error_type = None
@@ -152,7 +152,7 @@ class ACMEProtocolException(ModuleFailException):
elif content is not None:
add_msg = f" The raw result: {to_text(content)}"
super(ACMEProtocolException, self).__init__(f"{msg}.{add_msg}", **extras)
super().__init__(f"{msg}.{add_msg}", **extras)
self.problem: dict[str, t.Any] = {}
self.subproblems: list[dict[str, t.Any]] = []
self.error_code = error_code

View File

@@ -29,7 +29,7 @@ def read_file(fn: str | os.PathLike) -> bytes:
with open(fn, "rb") as f:
return f.read()
except Exception as e:
raise ModuleFailException(f'Error while reading file "{fn}": {e}')
raise ModuleFailException(f'Error while reading file "{fn}": {e}') from e
# This function was adapted from an earlier version of https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/uri.py
@@ -55,7 +55,7 @@ def write_file(
raise ModuleFailException(
f"failed to create temporary content file: {err}",
exception=traceback.format_exc(),
)
) from err
f.close()
checksum_src = None
checksum_dest = None
@@ -94,7 +94,7 @@ def write_file(
raise ModuleFailException(
f"failed to copy {tmpsrc} to {dest}: {err}",
exception=traceback.format_exc(),
)
) from err
os.remove(tmpsrc)
return changed

View File

@@ -60,13 +60,13 @@ def pem_to_der(
lines = pem_content.splitlines()
elif pem_filename is not None:
try:
with open(pem_filename, "rt") as f:
with open(pem_filename, "r", encoding="utf-8") as f:
lines = list(f)
except Exception as err:
raise ModuleFailException(
f"cannot load PEM file {pem_filename}: {err}",
exception=traceback.format_exc(),
)
) from err
else:
raise ModuleFailException(
"One of pem_filename and pem_content must be provided"

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__ = (

View File

@@ -43,16 +43,20 @@ valid_file_format = re.compile(r".*(\.)(yml|yaml|json)$")
def ecs_client_argument_spec() -> dict[str, t.Any]:
return dict(
entrust_api_user=dict(type="str", required=True),
entrust_api_key=dict(type="str", required=True, no_log=True),
entrust_api_client_cert_path=dict(type="path", required=True),
entrust_api_client_cert_key_path=dict(type="path", required=True, no_log=True),
entrust_api_specification_path=dict(
type="path",
default="https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml",
),
)
return {
"entrust_api_user": {"type": "str", "required": True},
"entrust_api_key": {"type": "str", "required": True, "no_log": True},
"entrust_api_client_cert_path": {"type": "path", "required": True},
"entrust_api_client_cert_key_path": {
"type": "path",
"required": True,
"no_log": True,
},
"entrust_api_specification_path": {
"type": "path",
"default": "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml",
},
}
class SessionConfigurationException(Exception):
@@ -182,8 +186,7 @@ class RestOperation:
if result or result == {}:
if result_code and result_code < 400:
return result
else:
raise RestOperationException(result)
raise RestOperationException(result)
# Raise a generic RestOperationException if this fails
raise RestOperationException(
@@ -323,9 +326,9 @@ class ECSSession:
except HTTPError as e:
raise SessionConfigurationException(
f"Error downloading specification from address '{entrust_api_specification_path}', received error code '{e.getcode()}'"
)
) from e
else:
with open(entrust_api_specification_path) as f:
with open(entrust_api_specification_path, "rb") as f:
if ".json" in entrust_api_specification_path:
self._spec = json.load(f)
elif (

View File

@@ -45,8 +45,7 @@ def restore_on_failure(
if backup_file is not None:
module.atomic_move(os.path.abspath(backup_file), os.path.abspath(path))
raise
else:
module.add_cleanup_file(backup_file)
module.add_cleanup_file(backup_file)
return backup_and_restore
@@ -91,9 +90,8 @@ def _restore_all_on_failure(
os.path.abspath(backup), os.path.abspath(destination)
)
raise
else:
for destination, backup in backups:
self.module.add_cleanup_file(backup)
for destination, backup in backups:
self.module.add_cleanup_file(backup)
return backup_and_restore
@@ -126,7 +124,7 @@ class OpensshModule(metaclass=abc.ABCMeta):
result["changed"] = self.changed
if self.module._diff:
if self.module._diff: # pylint: disable=protected-access
result["diff"] = self.diff
return result
@@ -219,7 +217,7 @@ class KeygenCommand:
serial_number: int | None,
signature_algorithm: str | None,
signing_key_path: str,
type: t.Literal["host", "user"] | None,
cert_type: t.Literal["host", "user"] | None,
time_parameters: OpensshCertificateTimeParameters,
use_agent: bool,
**kwargs,
@@ -235,7 +233,7 @@ class KeygenCommand:
args.extend(["-n", ",".join(principals)])
if serial_number is not None:
args.extend(["-z", str(serial_number)])
if type == "host":
if cert_type == "host":
args.extend(["-h"])
if use_agent:
args.extend(["-U"])
@@ -252,7 +250,7 @@ class KeygenCommand:
*,
private_key_path: str,
size: int,
type: str,
key_type: str,
comment: str | None,
**kwargs,
) -> tuple[int, str, str]:
@@ -264,7 +262,7 @@ class KeygenCommand:
"-b",
str(size),
"-t",
type,
key_type,
"-f",
private_key_path,
"-C",
@@ -313,7 +311,7 @@ class KeygenCommand:
except (IOError, OSError) as e:
raise ValueError(
f"The private key at {private_key_path} is not writeable preventing a comment update ({e})"
)
) from e
command = [self._bin_path, "-q"]
if force_new_format:
@@ -327,12 +325,12 @@ _PrivateKey = t.TypeVar("_PrivateKey", bound="PrivateKey")
class PrivateKey:
def __init__(
self, *, size: int, key_type: str, fingerprint: str, format: str = ""
self, *, size: int, key_type: str, fingerprint: str, key_format: str = ""
) -> None:
self._size = size
self._type = key_type
self._fingerprint = fingerprint
self._format = format
self._format = key_format
@property
def size(self) -> int:
@@ -428,11 +426,8 @@ class PublicKey:
@classmethod
def load(cls: t.Type[_PublicKey], path: str | os.PathLike) -> _PublicKey | None:
try:
with open(path, "r") as f:
properties = f.read().strip(" \n").split(" ", 2)
except (IOError, OSError):
raise
with open(path, "r", encoding="utf-8") as f:
properties = f.read().strip(" \n").split(" ", 2)
if len(properties) < 2:
return None
@@ -454,14 +449,14 @@ def parse_private_key_format(
*,
path: str | os.PathLike,
) -> t.Literal["SSH", "PKCS8", "PKCS1", ""]:
with open(path, "r") as file:
with open(path, "r", encoding="utf-8") as file:
header = file.readline().strip()
if header == "-----BEGIN OPENSSH PRIVATE KEY-----":
return "SSH"
elif header == "-----BEGIN PRIVATE KEY-----":
if header == "-----BEGIN PRIVATE KEY-----":
return "PKCS8"
elif header == "-----BEGIN RSA PRIVATE KEY-----":
if header == "-----BEGIN RSA PRIVATE KEY-----":
return "PKCS1"
return ""

View File

@@ -54,7 +54,7 @@ if t.TYPE_CHECKING:
class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
def __init__(self, *, module: AnsibleModule) -> None:
super(KeypairBackend, self).__init__(module=module)
super().__init__(module=module)
self.comment: str | None = self.module.params["comment"]
self.private_key_path: str = self.module.params["path"]
@@ -189,9 +189,9 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
def _should_generate(self) -> bool:
if self.original_private_key is None:
return True
elif self.regenerate == "never":
if self.regenerate == "never":
return False
elif self.regenerate == "fail":
if self.regenerate == "fail":
if not self._private_key_valid():
self.module.fail_json(
msg="Key has wrong type and/or size. Will not proceed. "
@@ -199,10 +199,9 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
+ "`partial_idempotence`, `full_idempotence` or `always`, or with `force=true`."
)
return False
elif self.regenerate in ("partial_idempotence", "full_idempotence"):
if self.regenerate in ("partial_idempotence", "full_idempotence"):
return not self._private_key_valid()
else:
return True
return True
def _private_key_valid(self) -> bool:
if self.original_private_key is None:
@@ -358,7 +357,7 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta):
class KeypairBackendOpensshBin(KeypairBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(KeypairBackendOpensshBin, self).__init__(module=module)
super().__init__(module=module)
if self.module.params["private_key_format"] != "auto":
self.module.fail_json(
@@ -371,7 +370,7 @@ class KeypairBackendOpensshBin(KeypairBackend):
self.ssh_keygen.generate_keypair(
private_key_path=private_key_path,
size=self.size,
type=self.type,
key_type=self.type,
comment=self.comment,
check_rc=True,
)
@@ -391,7 +390,7 @@ class KeypairBackendOpensshBin(KeypairBackend):
return PublicKey.from_string(public_key_content)
def _private_key_readable(self) -> bool:
rc, stdout, stderr = self.ssh_keygen.get_matching_public_key(
rc, _stdout, stderr = self.ssh_keygen.get_matching_public_key(
private_key_path=self.private_key_path, check_rc=False
)
return not (
@@ -425,7 +424,7 @@ class KeypairBackendOpensshBin(KeypairBackend):
class KeypairBackendCryptography(KeypairBackend):
def __init__(self, *, module: AnsibleModule) -> None:
super(KeypairBackendCryptography, self).__init__(module=module)
super().__init__(module=module)
if self.type == "rsa1":
self.module.fail_json(
@@ -489,7 +488,7 @@ class KeypairBackendCryptography(KeypairBackend):
size=keypair.size,
key_type=keypair.key_type,
fingerprint=keypair.fingerprint,
format=parse_private_key_format(path=self.private_key_path),
key_format=parse_private_key_format(path=self.private_key_path),
)
def _get_public_key(self) -> PublicKey | t.Literal[""]:
@@ -522,10 +521,9 @@ class KeypairBackendCryptography(KeypairBackend):
OpensshKeypair.load(
path=self.private_key_path, passphrase=None, no_public_key=True
)
return False
except (InvalidPrivateKeyFileError, InvalidPassphraseError):
return True
else:
return False
return True

View File

@@ -124,11 +124,9 @@ class OpensshCertificateTimeParameters:
def __eq__(self, other: object) -> bool:
if not isinstance(other, type(self)):
return NotImplemented
else:
return (
self._valid_from == other._valid_from
and self._valid_to == other._valid_to
)
return (
self._valid_from == other._valid_from and self._valid_to == other._valid_to
)
def __ne__(self, other: object) -> bool:
return not self == other
@@ -188,12 +186,11 @@ class OpensshCertificateTimeParameters:
return "always"
if dt == _FOREVER:
return "forever"
else:
return (
dt.isoformat().replace("+00:00", "")
if date_format == "human_readable"
else dt.strftime("%Y%m%d%H%M%S")
)
return (
dt.isoformat().replace("+00:00", "")
if date_format == "human_readable"
else dt.strftime("%Y%m%d%H%M%S")
)
if date_format == "timestamp":
td = dt - _ALWAYS
return int(
@@ -203,22 +200,17 @@ class OpensshCertificateTimeParameters:
@staticmethod
def to_datetime(time_string_or_timestamp: str | bytes | int) -> datetime:
try:
if isinstance(time_string_or_timestamp, (str, bytes)):
result = OpensshCertificateTimeParameters._time_string_to_datetime(
to_text(time_string_or_timestamp.strip())
)
elif isinstance(time_string_or_timestamp, int):
result = OpensshCertificateTimeParameters._timestamp_to_datetime(
time_string_or_timestamp
)
else:
raise ValueError(
f"Value must be of type (str, unicode, int) not {type(time_string_or_timestamp)}"
)
except ValueError:
raise
return result
if isinstance(time_string_or_timestamp, (str, bytes)):
return OpensshCertificateTimeParameters._time_string_to_datetime(
to_text(time_string_or_timestamp.strip())
)
if isinstance(time_string_or_timestamp, int):
return OpensshCertificateTimeParameters._timestamp_to_datetime(
time_string_or_timestamp
)
raise ValueError(
f"Value must be of type (str, unicode, int) not {type(time_string_or_timestamp)}"
)
@staticmethod
def _timestamp_to_datetime(timestamp: int) -> datetime:
@@ -228,8 +220,8 @@ class OpensshCertificateTimeParameters:
return _FOREVER
try:
return datetime.fromtimestamp(timestamp, tz=_datetime.timezone.utc)
except OverflowError:
raise ValueError
except OverflowError as e:
raise ValueError from e
@staticmethod
def _time_string_to_datetime(time_string: str) -> datetime:
@@ -382,16 +374,15 @@ class OpensshCertificateInfo(metaclass=abc.ABCMeta):
def cert_type(self) -> t.Literal["user", "host", ""]:
if self._cert_type == _USER_TYPE:
return "user"
elif self._cert_type == _HOST_TYPE:
if self._cert_type == _HOST_TYPE:
return "host"
else:
return ""
return ""
@cert_type.setter
def cert_type(self, cert_type: t.Literal["user", "host"] | int) -> None:
if cert_type == "user" or cert_type == _USER_TYPE:
if cert_type in ("user", _USER_TYPE):
self._cert_type = _USER_TYPE
elif cert_type == "host" or cert_type == _HOST_TYPE:
elif cert_type in ("host", _HOST_TYPE):
self._cert_type = _HOST_TYPE
else:
raise ValueError(f"{cert_type} is not a valid certificate type")
@@ -412,7 +403,7 @@ class OpensshCertificateInfo(metaclass=abc.ABCMeta):
class OpensshRSACertificateInfo(OpensshCertificateInfo):
def __init__(self, *, e: int | None = None, n: int | None = None, **kwargs) -> None:
super(OpensshRSACertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self.type_string = _SSH_TYPE_STRINGS["rsa"] + _CERT_SUFFIX_V01
self.e = e
self.n = n
@@ -444,7 +435,7 @@ class OpensshDSACertificateInfo(OpensshCertificateInfo):
y: int | None = None,
**kwargs,
) -> None:
super(OpensshDSACertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self.type_string = _SSH_TYPE_STRINGS["dsa"] + _CERT_SUFFIX_V01
self.p = p
self.q = q
@@ -476,7 +467,7 @@ class OpensshECDSACertificateInfo(OpensshCertificateInfo):
def __init__(
self, *, curve: bytes | None = None, public_key: bytes | None = None, **kwargs
):
super(OpensshECDSACertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self._curve = None
if curve is not None:
self.curve = curve
@@ -519,7 +510,7 @@ class OpensshECDSACertificateInfo(OpensshCertificateInfo):
class OpensshED25519CertificateInfo(OpensshCertificateInfo):
def __init__(self, *, pk: bytes | None = None, **kwargs) -> None:
super(OpensshED25519CertificateInfo, self).__init__(**kwargs)
super().__init__(**kwargs)
self.type_string = _SSH_TYPE_STRINGS["ed25519"] + _CERT_SUFFIX_V01
self.pk = pk
@@ -559,13 +550,13 @@ class OpensshCertificate:
with open(path, "rb") as cert_file:
data = cert_file.read()
except (IOError, OSError) as e:
raise ValueError(f"{path} cannot be opened for reading: {e}")
raise ValueError(f"{path} cannot be opened for reading: {e}") from e
try:
format_identifier, b64_cert = data.split(b" ")[:2]
cert = binascii.a2b_base64(b64_cert)
except (binascii.Error, ValueError):
raise ValueError("Certificate not in OpenSSH format")
except (binascii.Error, ValueError) as e:
raise ValueError("Certificate not in OpenSSH format") from e
for key_type, string in _SSH_TYPE_STRINGS.items():
if format_identifier == string + _CERT_SUFFIX_V01:
@@ -585,7 +576,7 @@ class OpensshCertificate:
cert_info = cls._parse_cert_info(pub_key_type, parser)
signature = parser.string()
except (TypeError, ValueError) as e:
raise ValueError(f"Invalid certificate data: {e}")
raise ValueError(f"Invalid certificate data: {e}") from e
if parser.remaining_bytes():
raise ValueError(
@@ -751,10 +742,9 @@ def apply_directives(directives: t.Iterable[str]) -> list[OpensshCertificateOpti
if "clear" in directives:
return []
else:
return list(
set(default_options()) - set(directive_to_option[d] for d in directives)
)
return list(
set(default_options()) - set(directive_to_option[d] for d in directives)
)
def default_options() -> list[OpensshCertificateOption]:

View File

@@ -19,6 +19,7 @@ try:
from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding, rsa
from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PrivateKey,
Ed25519PublicKey,
@@ -68,6 +69,10 @@ except ImportError:
CRYPTOGRAPHY_VERSION = "0.0"
_ALGORITHM_PARAMETERS = {}
from ansible_collections.community.crypto.plugins.module_utils._crypto.cryptography_support import (
is_potential_certificate_issuer_private_key,
)
if t.TYPE_CHECKING:
KeyFormat = t.Literal["SSH", "PKCS8", "PKCS1"]
@@ -309,10 +314,10 @@ class AsymmetricKeypair:
try:
self.verify(signature=self.sign(b"message"), data=b"message")
except InvalidSignatureError:
except InvalidSignatureError as e:
raise InvalidPublicKeyFileError(
"The private key and public key of this keypair do not match"
)
) from e
def __eq__(self, other: object) -> bool:
if not isinstance(other, AsymmetricKeypair):
@@ -368,7 +373,7 @@ class AsymmetricKeypair:
data, **_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"] # type: ignore
)
except TypeError as e:
raise InvalidDataError(e)
raise InvalidDataError(e) from e
def verify(self, *, signature: bytes, data: bytes) -> None:
"""Verifies that the signature associated with the provided data was signed
@@ -383,8 +388,8 @@ class AsymmetricKeypair:
data,
**_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"], # type: ignore
)
except InvalidSignature:
raise InvalidSignatureError
except InvalidSignature as e:
raise InvalidSignatureError from e
def update_passphrase(self, passphrase: bytes | None = None) -> None:
"""Updates the encryption algorithm of this key pair
@@ -661,10 +666,10 @@ def load_privatekey(
try:
privatekey_loader = privatekey_loaders[key_format]
except KeyError:
except KeyError as e:
raise InvalidKeyFormatError(
f"{key_format} is not a valid key format ({','.join(privatekey_loaders)})"
)
) from e
if not os.path.exists(path):
raise InvalidPrivateKeyFileError(f"No file was found at {path}")
@@ -673,32 +678,33 @@ def load_privatekey(
with open(path, "rb") as f:
content = f.read()
privatekey = privatekey_loader( # type: ignore
try:
privatekey = privatekey_loader(
data=content,
password=passphrase,
)
except ValueError as exc:
# Revert to PEM if key could not be loaded in SSH format
if key_format == "SSH":
try:
privatekey = privatekey_loaders["PEM"]( # type: ignore
except ValueError as exc:
# Revert to PEM if key could not be loaded in SSH format
if key_format == "SSH":
privatekey = privatekey_loaders["PEM"](
data=content,
password=passphrase,
)
except ValueError as e:
raise InvalidPrivateKeyFileError(e)
except TypeError as e:
raise InvalidPassphraseError(e)
except UnsupportedAlgorithm as e:
raise InvalidAlgorithmError(e)
else:
raise InvalidPrivateKeyFileError(exc)
else:
raise InvalidPrivateKeyFileError(exc) from exc
except ValueError as e:
raise InvalidPrivateKeyFileError(e) from e
except TypeError as e:
raise InvalidPassphraseError(e)
raise InvalidPassphraseError(e) from e
except UnsupportedAlgorithm as e:
raise InvalidAlgorithmError(e)
raise InvalidAlgorithmError(e) from e
if not is_potential_certificate_issuer_private_key(privatekey) or isinstance(
privatekey, Ed448PrivateKey
):
raise InvalidPrivateKeyFileError(
f"{privatekey} is not a supported private key type"
)
return privatekey
@@ -713,10 +719,10 @@ def load_publickey(
try:
publickey_loader = publickey_loaders[key_format]
except KeyError:
except KeyError as e:
raise InvalidKeyFormatError(
f"{key_format} is not a valid key format ({','.join(publickey_loaders)})"
)
) from e
if not os.path.exists(path):
raise InvalidPublicKeyFileError(f"No file was found at {path}")
@@ -729,9 +735,9 @@ def load_publickey(
data=content,
)
except ValueError as e:
raise InvalidPublicKeyFileError(e)
raise InvalidPublicKeyFileError(e) from e
except UnsupportedAlgorithm as e:
raise InvalidAlgorithmError(e)
raise InvalidAlgorithmError(e) from e
return publickey
@@ -749,8 +755,7 @@ def compare_publickeys(pk1: PublicKeyTypes, pk2: PublicKeyTypes) -> bool:
serialization.Encoding.Raw, serialization.PublicFormat.Raw
)
return a_bytes == b_bytes
else:
return pk1.public_numbers() == pk2.public_numbers() # type: ignore
return pk1.public_numbers() == pk2.public_numbers() # type: ignore
def compare_encryption_algorithms(
@@ -761,12 +766,11 @@ def compare_encryption_algorithms(
ea2, serialization.NoEncryption
):
return True
elif isinstance(ea1, serialization.BestAvailableEncryption) and isinstance(
if isinstance(ea1, serialization.BestAvailableEncryption) and isinstance(
ea2, serialization.BestAvailableEncryption
):
return ea1.password == ea2.password
else:
return False
return False
def get_encryption_algorithm(
@@ -775,7 +779,7 @@ def get_encryption_algorithm(
try:
return serialization.BestAvailableEncryption(passphrase)
except ValueError as e:
raise InvalidPassphraseError(e)
raise InvalidPassphraseError(e) from e
def validate_comment(comment: str) -> None:
@@ -796,7 +800,7 @@ def extract_comment(path: str | os.PathLike) -> str:
else:
comment = ""
except (IOError, OSError) as e:
raise InvalidPublicKeyFileError(e)
raise InvalidPublicKeyFileError(e) from e
return comment

View File

@@ -177,10 +177,9 @@ class OpensshParser:
def _check_position(self, offset: int) -> int:
if self._pos + offset > len(self._data):
raise ValueError(f"Insufficient data remaining at position: {self._pos}")
elif self._pos + offset < 0:
if self._pos + offset < 0:
raise ValueError("Position cannot be less than zero.")
else:
return self._pos + offset
return self._pos + offset
@classmethod
def signature_data(cls, *, signature_string: bytes) -> dict[str, bytes | int]:
@@ -306,7 +305,9 @@ class _OpensshWriter:
try:
self.string(",".join(value).encode("ASCII"))
except UnicodeEncodeError as e:
raise ValueError(f"Name-list's must consist of US-ASCII characters: {e}")
raise ValueError(
f"Name-list's must consist of US-ASCII characters: {e}"
) from e
return self

View File

@@ -41,7 +41,7 @@ def parse_serial(value: str | bytes) -> int:
except ValueError as exc:
raise ValueError(
f"The {i + 1}{th(i + 1)} part {part!r} is not a hexadecimal number in range [0, 255]: {exc}"
)
) from exc
result = (result << 8) | part_value
return result

View File

@@ -102,8 +102,7 @@ def convert_relative_to_datetime(
if parsed_result.group("prefix") == "+":
return now + offset
else:
return now - offset
return now - offset
def get_relative_time_option(