Reformat everything with black.

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

View File

@@ -38,41 +38,41 @@ try:
HAS_OPENSSH_SUPPORT = True
_ALGORITHM_PARAMETERS = {
'rsa': {
'default_size': 2048,
'valid_sizes': range(1024, 16384),
'signer_params': {
'padding': padding.PSS(
"rsa": {
"default_size": 2048,
"valid_sizes": range(1024, 16384),
"signer_params": {
"padding": padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
'algorithm': hashes.SHA256(),
"algorithm": hashes.SHA256(),
},
},
'dsa': {
'default_size': 1024,
'valid_sizes': [1024],
'signer_params': {
'algorithm': hashes.SHA256(),
"dsa": {
"default_size": 1024,
"valid_sizes": [1024],
"signer_params": {
"algorithm": hashes.SHA256(),
},
},
'ed25519': {
'default_size': 256,
'valid_sizes': [256],
'signer_params': {},
"ed25519": {
"default_size": 256,
"valid_sizes": [256],
"signer_params": {},
},
'ecdsa': {
'default_size': 256,
'valid_sizes': [256, 384, 521],
'signer_params': {
'signature_algorithm': ec.ECDSA(hashes.SHA256()),
"ecdsa": {
"default_size": 256,
"valid_sizes": [256, 384, 521],
"signer_params": {
"signature_algorithm": ec.ECDSA(hashes.SHA256()),
},
'curves': {
"curves": {
256: ec.SECP256R1(),
384: ec.SECP384R1(),
521: ec.SECP521R1(),
}
}
},
},
}
except ImportError:
HAS_OPENSSH_PRIVATE_FORMAT = False
@@ -80,7 +80,7 @@ except ImportError:
CRYPTOGRAPHY_VERSION = "0.0"
_ALGORITHM_PARAMETERS = {}
_TEXT_ENCODING = 'UTF-8'
_TEXT_ENCODING = "UTF-8"
class OpenSSHError(Exception):
@@ -131,26 +131,25 @@ class AsymmetricKeypair(object):
"""Container for newly generated asymmetric key pairs or those loaded from existing files"""
@classmethod
def generate(cls, keytype='rsa', size=None, passphrase=None):
def generate(cls, keytype="rsa", size=None, passphrase=None):
"""Returns an Asymmetric_Keypair object generated with the supplied parameters
or defaults to an unencrypted RSA-2048 key
or defaults to an unencrypted RSA-2048 key
:keytype: One of rsa, dsa, ecdsa, ed25519
:size: The key length for newly generated keys
:passphrase: Secret of type Bytes used to encrypt the private key being generated
:keytype: One of rsa, dsa, ecdsa, ed25519
:size: The key length for newly generated keys
:passphrase: Secret of type Bytes used to encrypt the private key being generated
"""
if keytype not in _ALGORITHM_PARAMETERS.keys():
raise InvalidKeyTypeError(
"%s is not a valid keytype. Valid keytypes are %s" % (
keytype, ", ".join(_ALGORITHM_PARAMETERS.keys())
)
"%s is not a valid keytype. Valid keytypes are %s"
% (keytype, ", ".join(_ALGORITHM_PARAMETERS.keys()))
)
if not size:
size = _ALGORITHM_PARAMETERS[keytype]['default_size']
size = _ALGORITHM_PARAMETERS[keytype]["default_size"]
else:
if size not in _ALGORITHM_PARAMETERS[keytype]['valid_sizes']:
if size not in _ALGORITHM_PARAMETERS[keytype]["valid_sizes"]:
raise InvalidKeySizeError(
"%s is not a valid key size for %s keys" % (size, keytype)
)
@@ -160,7 +159,7 @@ class AsymmetricKeypair(object):
else:
encryption_algorithm = serialization.NoEncryption()
if keytype == 'rsa':
if keytype == "rsa":
privatekey = rsa.generate_private_key(
# Public exponent should always be 65537 to prevent issues
# if improper padding is used during signing
@@ -168,16 +167,16 @@ class AsymmetricKeypair(object):
key_size=size,
backend=backend,
)
elif keytype == 'dsa':
elif keytype == "dsa":
privatekey = dsa.generate_private_key(
key_size=size,
backend=backend,
)
elif keytype == 'ed25519':
elif keytype == "ed25519":
privatekey = Ed25519PrivateKey.generate()
elif keytype == 'ecdsa':
elif keytype == "ecdsa":
privatekey = ec.generate_private_key(
_ALGORITHM_PARAMETERS['ecdsa']['curves'][size],
_ALGORITHM_PARAMETERS["ecdsa"]["curves"][size],
backend=backend,
)
@@ -188,18 +187,25 @@ class AsymmetricKeypair(object):
size=size,
privatekey=privatekey,
publickey=publickey,
encryption_algorithm=encryption_algorithm
encryption_algorithm=encryption_algorithm,
)
@classmethod
def load(cls, path, passphrase=None, private_key_format='PEM', public_key_format='PEM', no_public_key=False):
def load(
cls,
path,
passphrase=None,
private_key_format="PEM",
public_key_format="PEM",
no_public_key=False,
):
"""Returns an Asymmetric_Keypair object loaded from the supplied file path
:path: A path to an existing private key to be loaded
:passphrase: Secret of type bytes used to decrypt the private key being loaded
:private_key_format: Format of private key to be loaded
:public_key_format: Format of public key to be loaded
:no_public_key: Set 'True' to only load a private key and automatically populate the matching public key
:path: A path to an existing private key to be loaded
:passphrase: Secret of type bytes used to decrypt the private key being loaded
:private_key_format: Format of private key to be loaded
:public_key_format: Format of public key to be loaded
:no_public_key: Set 'True' to only load a private key and automatically populate the matching public key
"""
if passphrase:
@@ -211,40 +217,42 @@ class AsymmetricKeypair(object):
if no_public_key:
publickey = privatekey.public_key()
else:
publickey = load_publickey(path + '.pub', public_key_format)
publickey = load_publickey(path + ".pub", public_key_format)
# Ed25519 keys are always of size 256 and do not have a key_size attribute
if isinstance(privatekey, Ed25519PrivateKey):
size = _ALGORITHM_PARAMETERS['ed25519']['default_size']
size = _ALGORITHM_PARAMETERS["ed25519"]["default_size"]
else:
size = privatekey.key_size
if isinstance(privatekey, rsa.RSAPrivateKey):
keytype = 'rsa'
keytype = "rsa"
elif isinstance(privatekey, dsa.DSAPrivateKey):
keytype = 'dsa'
keytype = "dsa"
elif isinstance(privatekey, ec.EllipticCurvePrivateKey):
keytype = 'ecdsa'
keytype = "ecdsa"
elif isinstance(privatekey, Ed25519PrivateKey):
keytype = 'ed25519'
keytype = "ed25519"
else:
raise InvalidKeyTypeError("Key type '%s' is not supported" % type(privatekey))
raise InvalidKeyTypeError(
"Key type '%s' is not supported" % type(privatekey)
)
return cls(
keytype=keytype,
size=size,
privatekey=privatekey,
publickey=publickey,
encryption_algorithm=encryption_algorithm
encryption_algorithm=encryption_algorithm,
)
def __init__(self, keytype, size, privatekey, publickey, encryption_algorithm):
"""
:keytype: One of rsa, dsa, ecdsa, ed25519
:size: The key length for the private key of this key pair
:privatekey: Private key object of this key pair
:publickey: Public key object of this key pair
:encryption_algorithm: Hashed secret used to encrypt the private key of this key pair
:keytype: One of rsa, dsa, ecdsa, ed25519
:size: The key length for the private key of this key pair
:privatekey: Private key object of this key pair
:publickey: Public key object of this key pair
:encryption_algorithm: Hashed secret used to encrypt the private key of this key pair
"""
self.__size = size
@@ -254,7 +262,7 @@ class AsymmetricKeypair(object):
self.__encryption_algorithm = encryption_algorithm
try:
self.verify(self.sign(b'message'), b'message')
self.verify(self.sign(b"message"), b"message")
except InvalidSignatureError:
raise InvalidPublicKeyFileError(
"The private key and public key of this keypair do not match"
@@ -264,8 +272,11 @@ class AsymmetricKeypair(object):
if not isinstance(other, AsymmetricKeypair):
return NotImplemented
return (compare_publickeys(self.public_key, other.public_key) and
compare_encryption_algorithms(self.encryption_algorithm, other.encryption_algorithm))
return compare_publickeys(
self.public_key, other.public_key
) and compare_encryption_algorithms(
self.encryption_algorithm, other.encryption_algorithm
)
def __ne__(self, other):
return not self == other
@@ -303,13 +314,12 @@ class AsymmetricKeypair(object):
def sign(self, data):
"""Returns signature of data signed with the private key of this key pair
:data: byteslike data to sign
:data: byteslike data to sign
"""
try:
signature = self.__privatekey.sign(
data,
**_ALGORITHM_PARAMETERS[self.__keytype]['signer_params']
data, **_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"]
)
except TypeError as e:
raise InvalidDataError(e)
@@ -318,16 +328,16 @@ class AsymmetricKeypair(object):
def verify(self, signature, data):
"""Verifies that the signature associated with the provided data was signed
by the private key of this key pair.
by the private key of this key pair.
:signature: signature to verify
:data: byteslike data signed by the provided signature
:signature: signature to verify
:data: byteslike data signed by the provided signature
"""
try:
return self.__publickey.verify(
signature,
data,
**_ALGORITHM_PARAMETERS[self.__keytype]['signer_params']
**_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"]
)
except InvalidSignature:
raise InvalidSignatureError
@@ -335,7 +345,7 @@ class AsymmetricKeypair(object):
def update_passphrase(self, passphrase=None):
"""Updates the encryption algorithm of this key pair
:passphrase: Byte secret used to encrypt this key pair
:passphrase: Byte secret used to encrypt this key pair
"""
if passphrase:
@@ -348,20 +358,20 @@ class OpensshKeypair(object):
"""Container for OpenSSH encoded asymmetric key pairs"""
@classmethod
def generate(cls, keytype='rsa', size=None, passphrase=None, comment=None):
def generate(cls, keytype="rsa", size=None, passphrase=None, comment=None):
"""Returns an Openssh_Keypair object generated using the supplied parameters or defaults to a RSA-2048 key
:keytype: One of rsa, dsa, ecdsa, ed25519
:size: The key length for newly generated keys
:passphrase: Secret of type Bytes used to encrypt the newly generated private key
:comment: Comment for a newly generated OpenSSH public key
:keytype: One of rsa, dsa, ecdsa, ed25519
:size: The key length for newly generated keys
:passphrase: Secret of type Bytes used to encrypt the newly generated private key
:comment: Comment for a newly generated OpenSSH public key
"""
if comment is None:
comment = "%s@%s" % (getuser(), gethostname())
asym_keypair = AsymmetricKeypair.generate(keytype, size, passphrase)
openssh_privatekey = cls.encode_openssh_privatekey(asym_keypair, 'SSH')
openssh_privatekey = cls.encode_openssh_privatekey(asym_keypair, "SSH")
openssh_publickey = cls.encode_openssh_publickey(asym_keypair, comment)
fingerprint = calculate_fingerprint(openssh_publickey)
@@ -377,18 +387,20 @@ class OpensshKeypair(object):
def load(cls, path, passphrase=None, no_public_key=False):
"""Returns an Openssh_Keypair object loaded from the supplied file path
:path: A path to an existing private key to be loaded
:passphrase: Secret used to decrypt the private key being loaded
:no_public_key: Set 'True' to only load a private key and automatically populate the matching public key
:path: A path to an existing private key to be loaded
:passphrase: Secret used to decrypt the private key being loaded
:no_public_key: Set 'True' to only load a private key and automatically populate the matching public key
"""
if no_public_key:
comment = ""
else:
comment = extract_comment(path + '.pub')
comment = extract_comment(path + ".pub")
asym_keypair = AsymmetricKeypair.load(path, passphrase, 'SSH', 'SSH', no_public_key)
openssh_privatekey = cls.encode_openssh_privatekey(asym_keypair, 'SSH')
asym_keypair = AsymmetricKeypair.load(
path, passphrase, "SSH", "SSH", no_public_key
)
openssh_privatekey = cls.encode_openssh_privatekey(asym_keypair, "SSH")
openssh_publickey = cls.encode_openssh_publickey(asym_keypair, comment)
fingerprint = calculate_fingerprint(openssh_publickey)
@@ -404,29 +416,33 @@ class OpensshKeypair(object):
def encode_openssh_privatekey(asym_keypair, key_format):
"""Returns an OpenSSH encoded private key for a given keypair
:asym_keypair: Asymmetric_Keypair from the private key is extracted
:key_format: Format of the encoded private key.
:asym_keypair: Asymmetric_Keypair from the private key is extracted
:key_format: Format of the encoded private key.
"""
if key_format == 'SSH':
if key_format == "SSH":
# Default to PEM format if SSH not available
if not HAS_OPENSSH_PRIVATE_FORMAT:
privatekey_format = serialization.PrivateFormat.PKCS8
else:
privatekey_format = serialization.PrivateFormat.OpenSSH
elif key_format == 'PKCS8':
elif key_format == "PKCS8":
privatekey_format = serialization.PrivateFormat.PKCS8
elif key_format == 'PKCS1':
if asym_keypair.key_type == 'ed25519':
raise InvalidKeyFormatError("ed25519 keys cannot be represented in PKCS1 format")
elif key_format == "PKCS1":
if asym_keypair.key_type == "ed25519":
raise InvalidKeyFormatError(
"ed25519 keys cannot be represented in PKCS1 format"
)
privatekey_format = serialization.PrivateFormat.TraditionalOpenSSL
else:
raise InvalidKeyFormatError("The accepted private key formats are SSH, PKCS8, and PKCS1")
raise InvalidKeyFormatError(
"The accepted private key formats are SSH, PKCS8, and PKCS1"
)
encoded_privatekey = asym_keypair.private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=privatekey_format,
encryption_algorithm=asym_keypair.encryption_algorithm
encryption_algorithm=asym_keypair.encryption_algorithm,
)
return encoded_privatekey
@@ -435,8 +451,8 @@ class OpensshKeypair(object):
def encode_openssh_publickey(asym_keypair, comment):
"""Returns an OpenSSH encoded public key for a given keypair
:asym_keypair: Asymmetric_Keypair from the public key is extracted
:comment: Comment to apply to the end of the returned OpenSSH encoded public key
:asym_keypair: Asymmetric_Keypair from the public key is extracted
:comment: Comment to apply to the end of the returned OpenSSH encoded public key
"""
encoded_publickey = asym_keypair.public_key.public_bytes(
encoding=serialization.Encoding.OpenSSH,
@@ -445,17 +461,21 @@ class OpensshKeypair(object):
validate_comment(comment)
encoded_publickey += (" %s" % comment).encode(encoding=_TEXT_ENCODING) if comment else b''
encoded_publickey += (
(" %s" % comment).encode(encoding=_TEXT_ENCODING) if comment else b""
)
return encoded_publickey
def __init__(self, asym_keypair, openssh_privatekey, openssh_publickey, fingerprint, comment):
def __init__(
self, asym_keypair, openssh_privatekey, openssh_publickey, fingerprint, comment
):
"""
:asym_keypair: An Asymmetric_Keypair object from which the OpenSSH encoded keypair is derived
:openssh_privatekey: An OpenSSH encoded private key
:openssh_privatekey: An OpenSSH encoded public key
:fingerprint: The fingerprint of the OpenSSH encoded public key of this keypair
:comment: Comment applied to the OpenSSH public key of this keypair
:asym_keypair: An Asymmetric_Keypair object from which the OpenSSH encoded keypair is derived
:openssh_privatekey: An OpenSSH encoded private key
:openssh_privatekey: An OpenSSH encoded public key
:fingerprint: The fingerprint of the OpenSSH encoded public key of this keypair
:comment: Comment applied to the OpenSSH public key of this keypair
"""
self.__asym_keypair = asym_keypair
@@ -468,7 +488,10 @@ class OpensshKeypair(object):
if not isinstance(other, OpensshKeypair):
return NotImplemented
return self.asymmetric_keypair == other.asymmetric_keypair and self.comment == other.comment
return (
self.asymmetric_keypair == other.asymmetric_keypair
and self.comment == other.comment
)
@property
def asymmetric_keypair(self):
@@ -516,53 +539,59 @@ class OpensshKeypair(object):
def comment(self, comment):
"""Updates the comment applied to the OpenSSH formatted public key of this key pair
:comment: Text to update the OpenSSH public key comment
:comment: Text to update the OpenSSH public key comment
"""
validate_comment(comment)
self.__comment = comment
encoded_comment = (" %s" % self.__comment).encode(encoding=_TEXT_ENCODING) if self.__comment else b''
self.__openssh_publickey = b' '.join(self.__openssh_publickey.split(b' ', 2)[:2]) + encoded_comment
encoded_comment = (
(" %s" % self.__comment).encode(encoding=_TEXT_ENCODING)
if self.__comment
else b""
)
self.__openssh_publickey = (
b" ".join(self.__openssh_publickey.split(b" ", 2)[:2]) + encoded_comment
)
return self.__openssh_publickey
def update_passphrase(self, passphrase):
"""Updates the passphrase used to encrypt the private key of this keypair
:passphrase: Text secret used for encryption
:passphrase: Text secret used for encryption
"""
self.__asym_keypair.update_passphrase(passphrase)
self.__openssh_privatekey = OpensshKeypair.encode_openssh_privatekey(self.__asym_keypair, 'SSH')
self.__openssh_privatekey = OpensshKeypair.encode_openssh_privatekey(
self.__asym_keypair, "SSH"
)
def load_privatekey(path, passphrase, key_format):
privatekey_loaders = {
'PEM': serialization.load_pem_private_key,
'DER': serialization.load_der_private_key,
"PEM": serialization.load_pem_private_key,
"DER": serialization.load_der_private_key,
}
# OpenSSH formatted private keys are not available in Cryptography <3.0
if hasattr(serialization, 'load_ssh_private_key'):
privatekey_loaders['SSH'] = serialization.load_ssh_private_key
if hasattr(serialization, "load_ssh_private_key"):
privatekey_loaders["SSH"] = serialization.load_ssh_private_key
else:
privatekey_loaders['SSH'] = serialization.load_pem_private_key
privatekey_loaders["SSH"] = serialization.load_pem_private_key
try:
privatekey_loader = privatekey_loaders[key_format]
except KeyError:
raise InvalidKeyFormatError(
"%s is not a valid key format (%s)" % (
key_format,
','.join(privatekey_loaders.keys())
)
"%s is not a valid key format (%s)"
% (key_format, ",".join(privatekey_loaders.keys()))
)
if not os.path.exists(path):
raise InvalidPrivateKeyFileError("No file was found at %s" % path)
try:
with open(path, 'rb') as f:
with open(path, "rb") as f:
content = f.read()
privatekey = privatekey_loader(
@@ -573,9 +602,9 @@ def load_privatekey(path, passphrase, key_format):
except ValueError as e:
# Revert to PEM if key could not be loaded in SSH format
if key_format == 'SSH':
if key_format == "SSH":
try:
privatekey = privatekey_loaders['PEM'](
privatekey = privatekey_loaders["PEM"](
data=content,
password=passphrase,
backend=backend,
@@ -598,26 +627,24 @@ def load_privatekey(path, passphrase, key_format):
def load_publickey(path, key_format):
publickey_loaders = {
'PEM': serialization.load_pem_public_key,
'DER': serialization.load_der_public_key,
'SSH': serialization.load_ssh_public_key,
"PEM": serialization.load_pem_public_key,
"DER": serialization.load_der_public_key,
"SSH": serialization.load_ssh_public_key,
}
try:
publickey_loader = publickey_loaders[key_format]
except KeyError:
raise InvalidKeyFormatError(
"%s is not a valid key format (%s)" % (
key_format,
','.join(publickey_loaders.keys())
)
"%s is not a valid key format (%s)"
% (key_format, ",".join(publickey_loaders.keys()))
)
if not os.path.exists(path):
raise InvalidPublicKeyFileError("No file was found at %s" % path)
try:
with open(path, 'rb') as f:
with open(path, "rb") as f:
content = f.read()
publickey = publickey_loader(
@@ -646,10 +673,13 @@ def compare_publickeys(pk1, pk2):
def compare_encryption_algorithms(ea1, ea2):
if isinstance(ea1, serialization.NoEncryption) and isinstance(ea2, serialization.NoEncryption):
if isinstance(ea1, serialization.NoEncryption) and isinstance(
ea2, serialization.NoEncryption
):
return True
elif (isinstance(ea1, serialization.BestAvailableEncryption) and
isinstance(ea2, serialization.BestAvailableEncryption)):
elif isinstance(ea1, serialization.BestAvailableEncryption) and isinstance(
ea2, serialization.BestAvailableEncryption
):
return ea1.password == ea2.password
else:
return False
@@ -663,7 +693,7 @@ def get_encryption_algorithm(passphrase):
def validate_comment(comment):
if not hasattr(comment, 'encode'):
if not hasattr(comment, "encode"):
raise InvalidCommentError("%s cannot be encoded to text" % comment)
@@ -673,8 +703,8 @@ def extract_comment(path):
raise InvalidPublicKeyFileError("No file was found at %s" % path)
try:
with open(path, 'rb') as f:
fields = f.read().split(b' ', 2)
with open(path, "rb") as f:
fields = f.read().split(b" ", 2)
if len(fields) == 3:
comment = fields[2].decode(_TEXT_ENCODING)
else:
@@ -687,7 +717,9 @@ def extract_comment(path):
def calculate_fingerprint(openssh_publickey):
digest = hashes.Hash(hashes.SHA256(), backend=backend)
decoded_pubkey = b64decode(openssh_publickey.split(b' ')[1])
decoded_pubkey = b64decode(openssh_publickey.split(b" ")[1])
digest.update(decoded_pubkey)
return 'SHA256:%s' % b64encode(digest.finalize()).decode(encoding=_TEXT_ENCODING).rstrip('=')
return "SHA256:%s" % b64encode(digest.finalize()).decode(
encoding=_TEXT_ENCODING
).rstrip("=")