From 47745ab09031011df8910f0c94ca5612c737c49b Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 30 May 2023 16:39:29 +0200 Subject: [PATCH 01/26] Ensure comment is always empty string on export --- src/sshkey_tools/keys.py | 9 ++++++--- src/sshkey_tools/utils.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/sshkey_tools/keys.py b/src/sshkey_tools/keys.py index 9143cbf..46293cb 100644 --- a/src/sshkey_tools/keys.py +++ b/src/sshkey_tools/keys.py @@ -26,7 +26,7 @@ from cryptography.hazmat.primitives.asymmetric import rsa as _RSA from . import exceptions as _EX -from .utils import ensure_bytestring, ensure_string +from .utils import ensure_bytestring, ensure_string, nullsafe_getattr from .utils import md5_fingerprint as _FP_MD5 from .utils import sha256_fingerprint as _FP_SHA256 from .utils import sha512_fingerprint as _FP_SHA512 @@ -129,12 +129,15 @@ def __init__( _SERIALIZATION.Encoding.OpenSSH, _SERIALIZATION.PublicFormat.OpenSSH, ] + + # Ensure comment is not None + self.comment = nullsafe_getattr(self, "comment", "") @classmethod def from_class( cls, key_class: PubkeyClasses, - comment: Union[str, bytes] = None, + comment: Union[str, bytes] = "", key_type: Union[str, bytes] = None, ) -> "PublicKey": """ @@ -266,7 +269,7 @@ def to_string(self, encoding: str = "utf-8") -> str: return " ".join( [ ensure_string(self.serialize(), encoding), - ensure_string(getattr(self, "comment", ""), encoding), + ensure_string(nullsafe_getattr(self, "comment", ""), encoding), ] ) diff --git a/src/sshkey_tools/utils.py b/src/sshkey_tools/utils.py index 0d98234..4b21af2 100644 --- a/src/sshkey_tools/utils.py +++ b/src/sshkey_tools/utils.py @@ -233,6 +233,22 @@ def sha512_fingerprint(data: bytes, prefix: bool = True) -> str: ) +def nullsafe_getattr(obj, attr: str, default): + """ + Null-safe getattr, ensuring the result is not None. + If the result is None, the default value is returned instead. + + Args: + obj: The object + attr: The attribute to get + default: The default value + """ + att = getattr(obj, attr, default) + if att is None: + att = default + + return att + def join_dicts(*dicts) -> dict: """ Joins two or more dictionaries together. From 20e987b18bf33a1ed61e89981739762e823c186a Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 30 May 2023 17:19:19 +0200 Subject: [PATCH 02/26] Restored functionality to choose RSA hash algorithm Removed error when printing decoded certificate while trying to get private key fingerprint Updated docs Updated changelog --- README.md | 17 ++-- docs/cert.html | 32 ++++++-- docs/fields.html | 164 ++++++++++++++++++++++++------------- docs/index.html | 13 +-- docs/keys.html | 22 +++-- docs/utils.html | 83 ++++++++++++++++--- src/sshkey_tools/cert.py | 8 +- src/sshkey_tools/fields.py | 7 +- 8 files changed, 249 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 0b9ccd5..325cbdf 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,7 @@ Python package for managing OpenSSH keypairs and certificates ([protocol.CERTKEY - Export certificates to file, string or bytes # Roadmap -- [x] Rewrite certificate field functionality for simpler usage -- [ ] Re-add functionality for changing RSA hash method -- [ ] Add CLI functionality -- [ ] Convert to/from putty format (keys only) - +See issues for planned features and fixes # Installation @@ -49,6 +45,13 @@ pip3 install ./ # Documentation You can find the full documentation at [scheiblingco.github.io/sshkey-tools/](https://scheiblingco.github.io/sshkey-tools/) +## Building the documentation +```bash +pdoc3 src/sshkey_tools/ -o docs --html +cp -rf docs/sshkey_tools/* docs/ +rm -rf docs/sshkey_tools +``` + ## SSH Keypairs (generating, loading, exporting) ```python # Import the certificate classes @@ -308,6 +311,10 @@ certificate.sign() ``` ## Changelog +### 0.9.1 +- Updated documentation +- Fix for bug where exception would occur when trying to export a key without a comment set + ### 0.9 - Adjustments to certificate field handling for easier usage/syntax autocompletion - Updated testing diff --git a/docs/cert.html b/docs/cert.html index bba49f6..d14897f 100644 --- a/docs/cert.html +++ b/docs/cert.html @@ -540,9 +540,13 @@

Raises

bytes(self.header), bytes(self.fields), bytes(self.footer) ) - def sign(self) -> bool: + def sign(self, **kwargs) -> bool: """Sign the certificate + Args: + **kwargs: Arguments to pass to the signature signing method + ex. hash_alg for RSA signatures + Raises: _EX.NotSignedException: The certificate could not be signed @@ -550,7 +554,7 @@

Raises

bool: Whether successful """ if self.can_sign(): - self.footer.signature.sign(data=self.get_signable()) + self.footer.signature.sign(data=self.get_signable(), **kwargs) return True raise _EX.NotSignedException("There was an error while signing the certificate") @@ -2164,9 +2168,13 @@

Inherited members

bytes(self.header), bytes(self.fields), bytes(self.footer) ) - def sign(self) -> bool: + def sign(self, **kwargs) -> bool: """Sign the certificate + Args: + **kwargs: Arguments to pass to the signature signing method + ex. hash_alg for RSA signatures + Raises: _EX.NotSignedException: The certificate could not be signed @@ -2174,7 +2182,7 @@

Inherited members

bool: Whether successful """ if self.can_sign(): - self.footer.signature.sign(data=self.get_signable()) + self.footer.signature.sign(data=self.get_signable(), **kwargs) return True raise _EX.NotSignedException("There was an error while signing the certificate") @@ -2672,10 +2680,16 @@

Returns

-def sign(self) ‑> bool +def sign(self, **kwargs) ‑> bool

Sign the certificate

+

Args

+
+
**kwargs
+
Arguments to pass to the signature signing method +ex. hash_alg for RSA signatures
+

Raises

_EX.NotSignedException
@@ -2690,9 +2704,13 @@

Returns

Expand source code -
def sign(self) -> bool:
+
def sign(self, **kwargs) -> bool:
     """Sign the certificate
 
+    Args:
+        **kwargs: Arguments to pass to the signature signing method
+                  ex. hash_alg for RSA signatures
+
     Raises:
         _EX.NotSignedException: The certificate could not be signed
 
@@ -2700,7 +2718,7 @@ 

Returns

bool: Whether successful """ if self.can_sign(): - self.footer.signature.sign(data=self.get_signable()) + self.footer.signature.sign(data=self.get_signable(), **kwargs) return True raise _EX.NotSignedException("There was an error while signing the certificate")
diff --git a/docs/fields.html b/docs/fields.html index 26ffaf1..7f078dd 100644 --- a/docs/fields.html +++ b/docs/fields.html @@ -66,7 +66,7 @@

Module sshkey_tools.fields

long_to_bytes, random_keyid, random_serial, - str_to_timedelta, + str_to_time_delta, ) NoneType = type(None) @@ -168,7 +168,7 @@

Module sshkey_tools.fields

""" Validates if the field is set when required """ - if self.DEFAULT == self.value is None: + if self.DEFAULT and self.value is None: return _EX.InvalidFieldDataException( f"{self.get_name()} is a required field" ) @@ -294,22 +294,23 @@

Module sshkey_tools.fields

DEFAULT = b"" @classmethod - def encode(cls, value: bytes) -> bytes: + def encode(cls, value: bytes, encoding: str = "utf-8") -> bytes: """ Encodes a string or bytestring into a packed byte string Args: value (Union[str, bytes]): The string/bytestring to encode - encoding (str): The encoding to user for the string + encoding (str): The optional encoding, not used when passing + a byte value. Returns: bytes: Packed byte string containing the source data """ cls.__validate_type__(value, True) - return pack(">I", len(value)) + ensure_bytestring(value) + return pack(">I", len(value)) + ensure_bytestring(value, encoding) @staticmethod - def decode(data: bytes) -> Tuple[bytes, bytes]: + def decode(data: bytes, encoding: str = None) -> Tuple[bytes, bytes]: """ Unpacks the next string from a packed byte string @@ -321,6 +322,10 @@

Module sshkey_tools.fields

string and remainder of the data """ length = unpack(">I", data[:4])[0] + 4 + + if encoding is not None: + return ensure_string(data[4:length], encoding), data[length:] + return ensure_bytestring(data[4:length]), data[length:] @@ -344,8 +349,7 @@

Module sshkey_tools.fields

Returns: bytes: Packed byte string containing the source data """ - cls.__validate_type__(value, True) - return BytestringField.encode(ensure_bytestring(value, encoding)) + return super().encode(value, encoding) @staticmethod def decode(data: bytes, encoding: str = "utf-8") -> Tuple[str, bytes]: @@ -359,9 +363,7 @@

Module sshkey_tools.fields

tuple(bytes, bytes): The next block of bytes from the packed byte string and remainder of the data """ - value, data = BytestringField.decode(data) - - return value.decode(encoding), data + return BytestringField.decode(data, encoding) class Integer32Field(CertificateField): @@ -498,9 +500,9 @@

Module sshkey_tools.fields

if isinstance(value, str): if value == "forever": - return Integer64Field.encode(MAX_INT64) + return Integer64Field.encode(MAX_INT64 - 1) - value = int(datetime.now() + str_to_timedelta(value)) + value = int((datetime.now() + str_to_time_delta(value)).timestamp()) if isinstance(value, datetime): value = int(value.timestamp()) @@ -779,7 +781,7 @@

Module sshkey_tools.fields

return True -class NonceField(StringField): +class NonceField(BytestringField): """ Contains the nonce for the certificate, randomly generated this protects the integrity of the private key, especially @@ -1321,8 +1323,11 @@

Module sshkey_tools.fields

def __table__(self) -> tuple: msg = "No signature" - if self.is_signed: + if self.is_signed and self.private_key is not None: msg = f"Signed with private key {self.private_key.get_fingerprint()}" + + if self.is_signed and self.private_key is None: + msg = "Signed with: See pubkey fingerprint above" return ("Signature", msg) @@ -1379,7 +1384,7 @@

Module sshkey_tools.fields

""" return self.private_key is not None - def sign(self, data: bytes) -> None: + def sign(self, data: bytes, **kwargs) -> None: """ Placeholder signing function """ @@ -1931,22 +1936,23 @@

Inherited members

DEFAULT = b"" @classmethod - def encode(cls, value: bytes) -> bytes: + def encode(cls, value: bytes, encoding: str = "utf-8") -> bytes: """ Encodes a string or bytestring into a packed byte string Args: value (Union[str, bytes]): The string/bytestring to encode - encoding (str): The encoding to user for the string + encoding (str): The optional encoding, not used when passing + a byte value. Returns: bytes: Packed byte string containing the source data """ cls.__validate_type__(value, True) - return pack(">I", len(value)) + ensure_bytestring(value) + return pack(">I", len(value)) + ensure_bytestring(value, encoding) @staticmethod - def decode(data: bytes) -> Tuple[bytes, bytes]: + def decode(data: bytes, encoding: str = None) -> Tuple[bytes, bytes]: """ Unpacks the next string from a packed byte string @@ -1958,6 +1964,10 @@

Inherited members

string and remainder of the data """ length = unpack(">I", data[:4])[0] + 4 + + if encoding is not None: + return ensure_string(data[4:length], encoding), data[length:] + return ensure_bytestring(data[4:length]), data[length:]

Ancestors

@@ -1968,6 +1978,7 @@

Subclasses

Class variables

@@ -1984,7 +1995,7 @@

Class variables

Static methods

-def decode(data: bytes) ‑> Tuple[bytes, bytes] +def decode(data: bytes, encoding: str = None) ‑> Tuple[bytes, bytes]

Unpacks the next string from a packed byte string

@@ -2002,7 +2013,7 @@

Returns

Expand source code
@staticmethod
-def decode(data: bytes) -> Tuple[bytes, bytes]:
+def decode(data: bytes, encoding: str = None) -> Tuple[bytes, bytes]:
     """
     Unpacks the next string from a packed byte string
 
@@ -2014,11 +2025,15 @@ 

Returns

string and remainder of the data """ length = unpack(">I", data[:4])[0] + 4 + + if encoding is not None: + return ensure_string(data[4:length], encoding), data[length:] + return ensure_bytestring(data[4:length]), data[length:]
-def encode(value: bytes) ‑> bytes +def encode(value: bytes, encoding: str = 'utf-8') ‑> bytes

Encodes a string or bytestring into a packed byte string

@@ -2027,7 +2042,8 @@

Args

value : Union[str, bytes]
The string/bytestring to encode
encoding : str
-
The encoding to user for the string
+
The optional encoding, not used when passing +a byte value.

Returns

@@ -2039,19 +2055,20 @@

Returns

Expand source code
@classmethod
-def encode(cls, value: bytes) -> bytes:
+def encode(cls, value: bytes, encoding: str = "utf-8") -> bytes:
     """
     Encodes a string or bytestring into a packed byte string
 
     Args:
         value (Union[str, bytes]): The string/bytestring to encode
-        encoding (str): The encoding to user for the string
+        encoding (str): The optional encoding, not used when passing
+                        a byte value.
 
     Returns:
         bytes: Packed byte string containing the source data
     """
     cls.__validate_type__(value, True)
-    return pack(">I", len(value)) + ensure_bytestring(value)
+ return pack(">I", len(value)) + ensure_bytestring(value, encoding)
@@ -2367,7 +2384,7 @@

Class variables

""" Validates if the field is set when required """ - if self.DEFAULT == self.value is None: + if self.DEFAULT and self.value is None: return _EX.InvalidFieldDataException( f"{self.get_name()} is a required field" ) @@ -2893,9 +2910,9 @@

Inherited members

if isinstance(value, str): if value == "forever": - return Integer64Field.encode(MAX_INT64) + return Integer64Field.encode(MAX_INT64 - 1) - value = int(datetime.now() + str_to_timedelta(value)) + value = int((datetime.now() + str_to_time_delta(value)).timestamp()) if isinstance(value, datetime): value = int(value.timestamp()) @@ -3033,9 +3050,9 @@

Returns

if isinstance(value, str): if value == "forever": - return Integer64Field.encode(MAX_INT64) + return Integer64Field.encode(MAX_INT64 - 1) - value = int(datetime.now() + str_to_timedelta(value)) + value = int((datetime.now() + str_to_time_delta(value)).timestamp()) if isinstance(value, datetime): value = int(value.timestamp()) @@ -4761,7 +4778,7 @@

Inherited members

  • StringField:
    • decode
    • -
    • encode
    • +
    • encode
    • factory
    • from_decode
    • get_name
    • @@ -5387,7 +5404,7 @@

      Inherited members

      Expand source code -
      class NonceField(StringField):
      +
      class NonceField(BytestringField):
           """
           Contains the nonce for the certificate, randomly generated
           this protects the integrity of the private key, especially
      @@ -5415,7 +5432,6 @@ 

      Inherited members

      Ancestors

      @@ -5467,14 +5483,14 @@

      Returns

      Inherited members

      @@ -5608,7 +5624,7 @@

      Inherited members

    • StringField:
      • decode
      • -
      • encode
      • +
      • encode
      • factory
      • from_decode
      • get_name
      • @@ -5871,7 +5887,7 @@

        Inherited members

      • StringField:
        • decode
        • -
        • encode
        • +
        • encode
        • factory
        • from_decode
        • get_name
        • @@ -6381,8 +6397,11 @@

          Inherited members

          def __table__(self) -> tuple: msg = "No signature" - if self.is_signed: + if self.is_signed and self.private_key is not None: msg = f"Signed with private key {self.private_key.get_fingerprint()}" + + if self.is_signed and self.private_key is None: + msg = "Signed with: See pubkey fingerprint above" return ("Signature", msg) @@ -6439,7 +6458,7 @@

          Inherited members

          """ return self.private_key is not None - def sign(self, data: bytes) -> None: + def sign(self, data: bytes, **kwargs) -> None: """ Placeholder signing function """ @@ -6599,7 +6618,7 @@

          Methods

          -def sign(self, data: bytes) ‑> None +def sign(self, data: bytes, **kwargs) ‑> None

          Placeholder signing function

          @@ -6607,7 +6626,7 @@

          Methods

          Expand source code -
          def sign(self, data: bytes) -> None:
          +
          def sign(self, data: bytes, **kwargs) -> None:
               """
               Placeholder signing function
               """
          @@ -6658,8 +6677,7 @@ 

          Inherited members

          Returns: bytes: Packed byte string containing the source data """ - cls.__validate_type__(value, True) - return BytestringField.encode(ensure_bytestring(value, encoding)) + return super().encode(value, encoding) @staticmethod def decode(data: bytes, encoding: str = "utf-8") -> Tuple[str, bytes]: @@ -6673,9 +6691,7 @@

          Inherited members

          tuple(bytes, bytes): The next block of bytes from the packed byte string and remainder of the data """ - value, data = BytestringField.decode(data) - - return value.decode(encoding), data
          + return BytestringField.decode(data, encoding)

          Ancestors

          +

          Static methods

          +
          +
          +def encode(value: str, encoding: str = 'utf-8') +
          +
          +

          Encodes a string or bytestring into a packed byte string

          +

          Args

          +
          +
          value : Union[str, bytes]
          +
          The string/bytestring to encode
          +
          encoding : str
          +
          The encoding to user for the string
          +
          +

          Returns

          +
          +
          bytes
          +
          Packed byte string containing the source data
          +
          +
          + +Expand source code + +
          @classmethod
          +def encode(cls, value: str, encoding: str = "utf-8"):
          +    """
          +    Encodes a string or bytestring into a packed byte string
          +
          +    Args:
          +        value (Union[str, bytes]): The string/bytestring to encode
          +        encoding (str): The encoding to user for the string
          +
          +    Returns:
          +        bytes: Packed byte string containing the source data
          +    """
          +    return super().encode(value, encoding)
          +
          +
          +

          Inherited members

          Roadmap

          -
            -
          • [x] Rewrite certificate field functionality for simpler usage
          • -
          • [ ] Re-add functionality for changing RSA hash method
          • -
          • [ ] Add CLI functionality
          • -
          • [ ] Convert to/from putty format (keys only)
          • -
          +

          See issues for planned features and fixes

          Installation

          With pip

          pip3 install sshkey-tools
          @@ -481,6 +476,11 @@ 

          Loading, re-cre

          Changelog

          +

          0.9.1

          +
            +
          • Updated documentation
          • +
          • Fix for bug where exception would occur when trying to export a key without a comment set
          • +

          0.9

          • Adjustments to certificate field handling for easier usage/syntax autocompletion
          • @@ -585,6 +585,7 @@

            Index

          • Creating, signing and verifying certificates
          • Loading, re-creating and verifying existing certificates
          • Changelog
              +
            • 0.9.1
            • 0.9
            • 0.8.2
            • 0.8.1
            • diff --git a/docs/keys.html b/docs/keys.html index 93e41c9..afa1da6 100644 --- a/docs/keys.html +++ b/docs/keys.html @@ -55,7 +55,7 @@

              Module sshkey_tools.keys

              from cryptography.hazmat.primitives.asymmetric import rsa as _RSA from . import exceptions as _EX -from .utils import ensure_bytestring, ensure_string +from .utils import ensure_bytestring, ensure_string, nullsafe_getattr from .utils import md5_fingerprint as _FP_MD5 from .utils import sha256_fingerprint as _FP_SHA256 from .utils import sha512_fingerprint as _FP_SHA512 @@ -158,12 +158,15 @@

              Module sshkey_tools.keys

              _SERIALIZATION.Encoding.OpenSSH, _SERIALIZATION.PublicFormat.OpenSSH, ] + + # Ensure comment is not None + self.comment = nullsafe_getattr(self, "comment", "") @classmethod def from_class( cls, key_class: PubkeyClasses, - comment: Union[str, bytes] = None, + comment: Union[str, bytes] = "", key_type: Union[str, bytes] = None, ) -> "PublicKey": """ @@ -295,7 +298,7 @@

              Module sshkey_tools.keys

              return " ".join( [ ensure_string(self.serialize(), encoding), - ensure_string(getattr(self, "comment", ""), encoding), + ensure_string(nullsafe_getattr(self, "comment", ""), encoding), ] ) @@ -2893,12 +2896,15 @@

              Returns

              _SERIALIZATION.Encoding.OpenSSH, _SERIALIZATION.PublicFormat.OpenSSH, ] + + # Ensure comment is not None + self.comment = nullsafe_getattr(self, "comment", "") @classmethod def from_class( cls, key_class: PubkeyClasses, - comment: Union[str, bytes] = None, + comment: Union[str, bytes] = "", key_type: Union[str, bytes] = None, ) -> "PublicKey": """ @@ -3030,7 +3036,7 @@

              Returns

              return " ".join( [ ensure_string(self.serialize(), encoding), - ensure_string(getattr(self, "comment", ""), encoding), + ensure_string(nullsafe_getattr(self, "comment", ""), encoding), ] ) @@ -3104,7 +3110,7 @@

              Returns

              -def from_class(key_class: Union[cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey, cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey], comment: Union[str, bytes] = None, key_type: Union[str, bytes] = None) ‑> PublicKey +def from_class(key_class: Union[cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey, cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey], comment: Union[str, bytes] = '', key_type: Union[str, bytes] = None) ‑> PublicKey

              Creates a new SSH Public key from a cryptography class

              @@ -3135,7 +3141,7 @@

              Returns

              def from_class( cls, key_class: PubkeyClasses, - comment: Union[str, bytes] = None, + comment: Union[str, bytes] = "", key_type: Union[str, bytes] = None, ) -> "PublicKey": """ @@ -3380,7 +3386,7 @@

              Returns

              return " ".join( [ ensure_string(self.serialize(), encoding), - ensure_string(getattr(self, "comment", ""), encoding), + ensure_string(nullsafe_getattr(self, "comment", ""), encoding), ] )
    • diff --git a/docs/utils.html b/docs/utils.html index 4be0c87..e953cf9 100644 --- a/docs/utils.html +++ b/docs/utils.html @@ -33,13 +33,15 @@

      Module sshkey_tools.utils

      import hashlib as hl import sys import datetime -import pytimeparse2 + from base64 import b64encode from random import randint from secrets import randbits from typing import Dict, List, Union from uuid import uuid4 +from pytimeparse2 import parse as time_parse + NoneType = type(None) @@ -260,6 +262,22 @@

      Module sshkey_tools.utils

      ) +def nullsafe_getattr(obj, attr: str, default): + """ + Null-safe getattr, ensuring the result is not None. + If the result is None, the default value is returned instead. + + Args: + obj: The object + attr: The attribute to get + default: The default value + """ + att = getattr(obj, attr, default) + if att is None: + att = default + + return att + def join_dicts(*dicts) -> dict: """ Joins two or more dictionaries together. @@ -283,7 +301,7 @@

      Module sshkey_tools.utils

      return return_dict -def str_to_timedelta(str_delta: str) -> datetime.timedelta: +def str_to_time_delta(str_delta: str) -> datetime.timedelta: """Uses the package pytimeparse2 by wroberts/onegreyonewhite to convert a string into a timedelta object. Examples: @@ -328,10 +346,12 @@

      Module sshkey_tools.utils

      datetime.timedelta: The time delta object """ try: - parsed = pytimeparse2.parse(str_delta, as_timedelta=True, raise_exception=True) + parsed = time_parse(str_delta, as_timedelta=True, raise_exception=True) return parsed - except Exception as e: - raise ValueError(f"Could not parse time delta string {str_delta} : {e}") from e
      + except Exception as ex: + raise ValueError( + f"Could not parse time delta string {str_delta} : {ex}" + ) from ex
      @@ -718,6 +738,42 @@

      Returns

      ) +
      +def nullsafe_getattr(obj, attr: str, default) +
      +
      +

      Null-safe getattr, ensuring the result is not None. +If the result is None, the default value is returned instead.

      +

      Args

      +
      +
      obj
      +
      The object
      +
      attr
      +
      The attribute to get
      +
      default
      +
      The default value
      +
      +
      + +Expand source code + +
      def nullsafe_getattr(obj, attr: str, default):
      +    """
      +    Null-safe getattr, ensuring the result is not None.
      +    If the result is None, the default value is returned instead.
      +
      +    Args:
      +        obj: The object
      +        attr: The attribute to get
      +        default: The default value
      +    """
      +    att = getattr(obj, attr, default)
      +    if att is None:
      +        att = default
      +    
      +    return att
      +
      +
      def random_keyid() ‑> str
      @@ -840,8 +896,8 @@

      Returns

      ) -
      -def str_to_timedelta(str_delta: str) ‑> datetime.timedelta +
      +def str_to_time_delta(str_delta: str) ‑> datetime.timedelta

      Uses the package pytimeparse2 by wroberts/onegreyonewhite @@ -894,7 +950,7 @@

      Returns

      Expand source code -
      def str_to_timedelta(str_delta: str) -> datetime.timedelta:
      +
      def str_to_time_delta(str_delta: str) -> datetime.timedelta:
           """Uses the package pytimeparse2 by wroberts/onegreyonewhite
               to convert a string into a timedelta object.
               Examples:
      @@ -939,10 +995,12 @@ 

      Returns

      datetime.timedelta: The time delta object """ try: - parsed = pytimeparse2.parse(str_delta, as_timedelta=True, raise_exception=True) + parsed = time_parse(str_delta, as_timedelta=True, raise_exception=True) return parsed - except Exception as e: - raise ValueError(f"Could not parse time delta string {str_delta} : {e}") from e
      + except Exception as ex: + raise ValueError( + f"Could not parse time delta string {str_delta} : {ex}" + ) from ex
      @@ -972,11 +1030,12 @@

      Index

    • join_dicts
    • long_to_bytes
    • md5_fingerprint
    • +
    • nullsafe_getattr
    • random_keyid
    • random_serial
    • sha256_fingerprint
    • sha512_fingerprint
    • -
    • str_to_timedelta
    • +
    • str_to_time_delta
  • diff --git a/src/sshkey_tools/cert.py b/src/sshkey_tools/cert.py index 4a893a2..36d71f5 100644 --- a/src/sshkey_tools/cert.py +++ b/src/sshkey_tools/cert.py @@ -495,9 +495,13 @@ def get_signable(self) -> bytes: bytes(self.header), bytes(self.fields), bytes(self.footer) ) - def sign(self) -> bool: + def sign(self, **kwargs) -> bool: """Sign the certificate + Args: + **kwargs: Arguments to pass to the signature signing method + ex. hash_alg for RSA signatures + Raises: _EX.NotSignedException: The certificate could not be signed @@ -505,7 +509,7 @@ def sign(self) -> bool: bool: Whether successful """ if self.can_sign(): - self.footer.signature.sign(data=self.get_signable()) + self.footer.signature.sign(data=self.get_signable(), **kwargs) return True raise _EX.NotSignedException("There was an error while signing the certificate") diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index 72a029c..dd2fc41 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -1294,8 +1294,11 @@ def __init__(self, private_key: PrivateKey = None, signature: bytes = None): def __table__(self) -> tuple: msg = "No signature" - if self.is_signed: + if self.is_signed and self.private_key is not None: msg = f"Signed with private key {self.private_key.get_fingerprint()}" + + if self.is_signed and self.private_key is None: + msg = "Signed with: See pubkey fingerprint above" return ("Signature", msg) @@ -1352,7 +1355,7 @@ def can_sign(self): """ return self.private_key is not None - def sign(self, data: bytes) -> None: + def sign(self, data: bytes, **kwargs) -> None: """ Placeholder signing function """ From ab8783a37702b0e4aa03efe19b938b89d8f8a5e1 Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 30 May 2023 17:30:56 +0200 Subject: [PATCH 03/26] Added Rsa alg choice to documentation Added note about DSA deprecation and ECDSA flaws --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 325cbdf..729b8f4 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,20 @@ Python package for managing OpenSSH keypairs and certificates ([protocol.CERTKEYS](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)). Supported functionality includes: +# Notice +The DSA algorithm is considered deprecated and will be removed in a future version. If possible, use RSA, [(ECDSA)](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a) or ED25519 as a first-hand choice. + +Notice from OpenSSH: +``` +OpenSSH 7.0 and greater similarly disable the ssh-dss (DSA) public key algorithm. It too is weak and we recommend against its use. It can be re-enabled using the HostKeyAlgorithms configuration option: sshd_config(5) HostKeyAlgorithms +``` + +[ECDSA has some flaws](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a), especially when using short nonces or re-using nonces, it can still be used but exercise some caution in regards to nonces/re-signing identical data multiple times. + + # Features ### SSH Keys -- Supports RSA, DSA, ECDSA and ED25519 keys +- Supports RSA, DSA (Note: Deprecated), ECDSA and ED25519 keys - Import existing keys from file, string, byte data or [pyca/cryptography](https://github.com/pyca/cryptography) class - Generate new keys - Get public key from private keys @@ -127,6 +138,7 @@ b"\0xc\0a\........" The loaded private key objects can be used to sign bytestrings, and the public keys can be used to verify signatures on those ```python from sshkey_tools.keys import RsaPrivateKey, RsaPublicKey +from sshkey_tools.fields import RsaAlgs signable_data = b'This is a message that will be signed' @@ -136,6 +148,10 @@ pubkey = RsaPrivateKey.public_key # Sign the data signature = privkey.sign(signable_data) +# When using an RSA key for the signature, you can specify the hashing algorithm +# The default algorithm is SHA512 +signature = privkey.sign(signable_data, RsaAlgs.SHA512) + # Verify the signature (Throws exception if invalid) pubkey.verify(signable_data, signature) ``` From 4f331338942ce09141265fb79ba9a042118badb4 Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 30 May 2023 17:35:04 +0200 Subject: [PATCH 04/26] Linting --- src/sshkey_tools/fields.py | 2 +- src/sshkey_tools/keys.py | 2 +- src/sshkey_tools/utils.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index dd2fc41..81fd844 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -1296,7 +1296,7 @@ def __table__(self) -> tuple: msg = "No signature" if self.is_signed and self.private_key is not None: msg = f"Signed with private key {self.private_key.get_fingerprint()}" - + if self.is_signed and self.private_key is None: msg = "Signed with: See pubkey fingerprint above" diff --git a/src/sshkey_tools/keys.py b/src/sshkey_tools/keys.py index 46293cb..3c286a4 100644 --- a/src/sshkey_tools/keys.py +++ b/src/sshkey_tools/keys.py @@ -129,7 +129,7 @@ def __init__( _SERIALIZATION.Encoding.OpenSSH, _SERIALIZATION.PublicFormat.OpenSSH, ] - + # Ensure comment is not None self.comment = nullsafe_getattr(self, "comment", "") diff --git a/src/sshkey_tools/utils.py b/src/sshkey_tools/utils.py index 4b21af2..1eef0a1 100644 --- a/src/sshkey_tools/utils.py +++ b/src/sshkey_tools/utils.py @@ -246,9 +246,10 @@ def nullsafe_getattr(obj, attr: str, default): att = getattr(obj, attr, default) if att is None: att = default - + return att + def join_dicts(*dicts) -> dict: """ Joins two or more dictionaries together. From cb0f2629cfed21c94d212649e8046268dfff66c4 Mon Sep 17 00:00:00 2001 From: Lars Scheibling Date: Sat, 3 Jun 2023 22:45:07 +0200 Subject: [PATCH 05/26] Lock cryptography requirement to <41 Introduced deprecation notice --- README.md | 8 ++++++-- requirements.txt | 2 +- src/sshkey_tools/cert.py | 10 +++++++++- src/sshkey_tools/keys.py | 11 +++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 729b8f4..0e6ada8 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,20 @@ Python package for managing OpenSSH keypairs and certificates ([protocol.CERTKEYS](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)). Supported functionality includes: # Notice +The DSA algorithm has been deprecated and is removed in pyca/cryptography 41.x, meaning **version 0.9.* of this package will be the last to support DSA keys and certificates** for SSH. If there is any demand to reintroduce DSA support, please open an issue regarding this and we'll look into it. + +For now, **0.9.* will be restricted to version <41.1 of the cryptography package** and **0.10 will have its DSA support removed**. We've introduced a deprecation notice in version 0.9.3. + +## Background The DSA algorithm is considered deprecated and will be removed in a future version. If possible, use RSA, [(ECDSA)](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a) or ED25519 as a first-hand choice. -Notice from OpenSSH: +## Notice from OpenSSH: ``` OpenSSH 7.0 and greater similarly disable the ssh-dss (DSA) public key algorithm. It too is weak and we recommend against its use. It can be re-enabled using the HostKeyAlgorithms configuration option: sshd_config(5) HostKeyAlgorithms ``` [ECDSA has some flaws](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a), especially when using short nonces or re-using nonces, it can still be used but exercise some caution in regards to nonces/re-signing identical data multiple times. - # Features ### SSH Keys - Supports RSA, DSA (Note: Deprecated), ECDSA and ED25519 keys diff --git a/requirements.txt b/requirements.txt index d7c755c..2c27802 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ click -cryptography +cryptography<41.0.0 bcrypt enum34 PrettyTable diff --git a/src/sshkey_tools/cert.py b/src/sshkey_tools/cert.py index 36d71f5..ee04421 100644 --- a/src/sshkey_tools/cert.py +++ b/src/sshkey_tools/cert.py @@ -8,6 +8,7 @@ _EX.NoPrivateKeyException: The certificate contains no private key _EX.NotSignedException: The certificate is not signed and cannot be exported """ +import warnings from base64 import b64decode, b64encode from dataclasses import dataclass from typing import Tuple, Union @@ -577,9 +578,16 @@ class RsaCertificate(SSHCertificate): class DsaCertificate(SSHCertificate): - """The DSA Certificate class""" + """The DSA Certificate class (DEPRECATED)""" DEFAULT_KEY_TYPE = "ssh-dss-cert-v01@openssh.com" + + def __post_init__(self): + """Display the deprecation notice""" + warnings.warn( + "SSH DSA keys and certificates are deprecated and will be removed in version 0.10 of sshkey-tools", + stacklevel=2, + ) class EcdsaCertificate(SSHCertificate): diff --git a/src/sshkey_tools/keys.py b/src/sshkey_tools/keys.py index 3c286a4..a5fb829 100644 --- a/src/sshkey_tools/keys.py +++ b/src/sshkey_tools/keys.py @@ -1,6 +1,7 @@ """ Classes for handling SSH public/private keys """ +import warnings from base64 import b64decode from enum import Enum from struct import unpack @@ -613,6 +614,11 @@ def __init__( serialized=serialized, ) self.parameters = key.parameters().parameter_numbers() + + warnings.warn( + "SSH DSA keys and certificates are deprecated and will be removed in version 0.10 of sshkey-tools", + stacklevel=2, + ) @classmethod # pylint: disable=invalid-name @@ -665,6 +671,11 @@ def __init__(self, key: _DSA.DSAPrivateKey): public_key=DsaPublicKey(key.public_key()), private_numbers=key.private_numbers(), ) + + warnings.warn( + "SSH DSA keys and certificates are deprecated and will be removed in version 0.10 of sshkey-tools", + stacklevel=2, + ) @classmethod # pylint: disable=invalid-name,too-many-arguments From b0448b018e46211aac5788958e7e77de2ae691c8 Mon Sep 17 00:00:00 2001 From: Lars Scheibling Date: Wed, 7 Jun 2023 12:03:04 +0000 Subject: [PATCH 06/26] Removed DSA tests from unit tests Updated requirement for cryptography --- requirements.txt | 2 +- tests/test_keypairs.py | 128 ++--------------------------------------- 2 files changed, 5 insertions(+), 125 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2c27802..d7c755c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ click -cryptography<41.0.0 +cryptography bcrypt enum34 PrettyTable diff --git a/tests/test_keypairs.py b/tests/test_keypairs.py index 6b60fdb..69cc202 100644 --- a/tests/test_keypairs.py +++ b/tests/test_keypairs.py @@ -6,15 +6,12 @@ import shutil import unittest -from cryptography.hazmat.primitives.asymmetric import dsa as _DSA from cryptography.hazmat.primitives.asymmetric import ec as _EC from cryptography.hazmat.primitives.asymmetric import ed25519 as _ED25519 from cryptography.hazmat.primitives.asymmetric import rsa as _RSA import src.sshkey_tools.exceptions as _EX from src.sshkey_tools.keys import ( - DsaPrivateKey, - DsaPublicKey, EcdsaCurves, EcdsaPrivateKey, EcdsaPublicKey, @@ -30,7 +27,6 @@ class KeypairMethods(unittest.TestCase): def generateClasses(self): self.rsa_key = RsaPrivateKey.generate(2048) - self.dsa_key = DsaPrivateKey.generate() self.ecdsa_key = EcdsaPrivateKey.generate(EcdsaCurves.P256) self.ed25519_key = Ed25519PrivateKey.generate() @@ -45,9 +41,6 @@ def generateFiles(self, folder): os.system( f'ssh-keygen -t rsa -b 2048 -f tests/{folder}/rsa_key_sshkeygen -N "password" > /dev/null 2>&1' ) - os.system( - f'ssh-keygen -t dsa -b 1024 -f tests/{folder}/dsa_key_sshkeygen -N "" > /dev/null 2>&1' - ) os.system( f'ssh-keygen -t ecdsa -b 256 -f tests/{folder}/ecdsa_key_sshkeygen -N "" > /dev/null 2>&1' ) @@ -105,20 +98,20 @@ def test_fail_assertions(self): RsaPrivateKey.from_file( f"tests/{self.folder}/rsa_key_sshkeygen", "password" ), - DsaPrivateKey.from_file(f"tests/{self.folder}/dsa_key_sshkeygen"), + EcdsaPrivateKey.from_file(f"tests/{self.folder}/ecdsa_key_sshkeygen"), ) with self.assertRaises(AssertionError): self.assertEqualPublicKeys( RsaPublicKey, RsaPublicKey.from_file(f"tests/{self.folder}/rsa_key_sshkeygen.pub"), - DsaPublicKey.from_file(f"tests/{self.folder}/dsa_key_sshkeygen.pub"), + EcdsaPublicKey.from_file(f"tests/{self.folder}/ecdsa_key_sshkeygen.pub"), ) with self.assertRaises(AssertionError): self.assertEqualKeyFingerprint( f"tests/{self.folder}/rsa_key_sshkeygen", - f"tests/{self.folder}/dsa_key_sshkeygen", + f"tests/{self.folder}/ecdsa_key_sshkeygen", ) def test_successful_assertions(self): @@ -128,12 +121,6 @@ def test_successful_assertions(self): f"tests/{self.folder}/rsa_key_sshkeygen.pub", ) - self.assertTrue(os.path.isfile(f"tests/{self.folder}/dsa_key_sshkeygen")) - self.assertEqualKeyFingerprint( - f"tests/{self.folder}/dsa_key_sshkeygen", - f"tests/{self.folder}/dsa_key_sshkeygen.pub", - ) - self.assertTrue(os.path.isfile(f"tests/{self.folder}/ecdsa_key_sshkeygen")) self.assertEqualKeyFingerprint( f"tests/{self.folder}/ecdsa_key_sshkeygen", @@ -174,21 +161,6 @@ def test_rsa_incorrect_keysize(self): with self.assertRaises(ValueError): RsaPrivateKey.generate(256) - def test_dsa(self): - - key = DsaPrivateKey.generate() - - assert isinstance(key, DsaPrivateKey) - assert isinstance(key, PrivateKey) - assert isinstance(key.key, _DSA.DSAPrivateKey) - assert isinstance(key.private_numbers, _DSA.DSAPrivateNumbers) - - assert isinstance(key.public_key, DsaPublicKey) - assert isinstance(key.public_key, PublicKey) - assert isinstance(key.public_key.key, _DSA.DSAPublicKey) - assert isinstance(key.public_key.public_numbers, _DSA.DSAPublicNumbers) - assert isinstance(key.public_key.parameters, _DSA.DSAParameterNumbers) - def test_ecdsa(self): curves = [EcdsaCurves.P256, EcdsaCurves.P384, EcdsaCurves.P521] @@ -297,49 +269,6 @@ def test_rsa_files(self): f"tests/{self.folder}/rsa_key_sshkeygen.pub", ) - def test_dsa_files(self): - parent = PrivateKey.from_file(f"tests/{self.folder}/dsa_key_sshkeygen") - child = DsaPrivateKey.from_file(f"tests/{self.folder}/dsa_key_sshkeygen") - - parent_pub = PublicKey.from_file(f"tests/{self.folder}/dsa_key_sshkeygen.pub") - child_pub = DsaPublicKey.from_file(f"tests/{self.folder}/dsa_key_sshkeygen.pub") - - parent.to_file(f"tests/{self.folder}/dsa_key_saved_parent") - child.to_file(f"tests/{self.folder}/dsa_key_saved_child") - - parent_pub.to_file(f"tests/{self.folder}/dsa_key_saved_parent.pub") - child_pub.to_file(f"tests/{self.folder}/dsa_key_saved_child.pub") - - self.assertEqualPrivateKeys(DsaPrivateKey, DsaPublicKey, parent, child) - - self.assertEqualPublicKeys(DsaPublicKey, parent_pub, child_pub) - - self.assertEqualPublicKeys(DsaPublicKey, parent.public_key, child_pub) - - self.assertEqualKeyFingerprint( - f"tests/{self.folder}/dsa_key_sshkeygen", - f"tests/{self.folder}/dsa_key_saved_parent", - ) - - self.assertEqualKeyFingerprint( - f"tests/{self.folder}/dsa_key_sshkeygen.pub", - f"tests/{self.folder}/dsa_key_saved_parent.pub", - ) - - self.assertEqualKeyFingerprint( - f"tests/{self.folder}/dsa_key_saved_parent", - f"tests/{self.folder}/dsa_key_saved_child", - ) - - self.assertEqualKeyFingerprint( - f"tests/{self.folder}/dsa_key_saved_parent.pub", - f"tests/{self.folder}/dsa_key_sshkeygen.pub", - ) - self.assertEqualKeyFingerprint( - f"tests/{self.folder}/dsa_key_saved_parent.pub", - f"tests/{self.folder}/dsa_key_sshkeygen.pub", - ) - def test_ecdsa_files(self): parent = PrivateKey.from_file(f"tests/{self.folder}/ecdsa_key_sshkeygen") child = EcdsaPrivateKey.from_file(f"tests/{self.folder}/ecdsa_key_sshkeygen") @@ -438,7 +367,6 @@ def test_ed25519_files(self): class TestFromClass(KeypairMethods): def setUp(self): self.rsa_key = _RSA.generate_private_key(public_exponent=65537, key_size=2048) - self.dsa_key = _DSA.generate_private_key(key_size=1024) self.ecdsa_key = _EC.generate_private_key(curve=_EC.SECP384R1()) self.ed25519_key = _ED25519.Ed25519PrivateKey.generate() @@ -457,12 +385,6 @@ def test_rsa_from_class(self): self.assertEqualPrivateKeys(RsaPrivateKey, RsaPublicKey, parent, child) - def test_dsa_from_class(self): - parent = PrivateKey.from_class(self.dsa_key) - child = DsaPrivateKey.from_class(self.dsa_key) - - self.assertEqualPrivateKeys(DsaPrivateKey, DsaPublicKey, parent, child) - def test_ecdsa_from_class(self): parent = PrivateKey.from_class(self.ecdsa_key) child = EcdsaPrivateKey.from_class(self.ecdsa_key) @@ -513,30 +435,7 @@ def test_rsa_from_numbers(self): self.assertEqual(self.rsa_key.private_numbers.d, from_numbers.private_numbers.d) - def test_dsa_from_numbers(self): - from_numbers = DsaPrivateKey.from_numbers( - p=self.dsa_key.public_key.parameters.p, - q=self.dsa_key.public_key.parameters.q, - g=self.dsa_key.public_key.parameters.g, - y=self.dsa_key.public_key.public_numbers.y, - x=self.dsa_key.private_numbers.x, - ) - - from_numbers_pub = DsaPublicKey.from_numbers( - p=self.dsa_key.public_key.parameters.p, - q=self.dsa_key.public_key.parameters.q, - g=self.dsa_key.public_key.parameters.g, - y=self.dsa_key.public_key.public_numbers.y, - ) - - self.assertEqualPrivateKeys( - DsaPrivateKey, DsaPublicKey, self.dsa_key, from_numbers - ) - - self.assertEqualPublicKeys( - DsaPublicKey, from_numbers_pub, self.dsa_key.public_key - ) - + def test_ecdsa_from_numbers(self): from_numbers = EcdsaPrivateKey.from_numbers( curve=self.ecdsa_key.public_key.key.curve, @@ -599,16 +498,6 @@ def test_rsa_fingerprint(self): self.assertEqual(key.get_fingerprint(), sshkey_fingerprint) - def test_dsa_fingerprint(self): - key = DsaPrivateKey.from_file( - f"tests/{self.folder}/dsa_key_sshkeygen", - ) - - with os.popen(f"ssh-keygen -lf tests/{self.folder}/dsa_key_sshkeygen") as cmd: - sshkey_fingerprint = cmd.read().split(" ")[1] - - self.assertEqual(key.get_fingerprint(), sshkey_fingerprint) - def test_ecdsa_fingerprint(self): key = EcdsaPrivateKey.from_file( f"tests/{self.folder}/ecdsa_key_sshkeygen", @@ -646,15 +535,6 @@ def test_rsa_signature(self): with self.assertRaises(_EX.InvalidSignatureException): self.rsa_key.public_key.verify(data, signature + b"\x00") - def test_dsa_signature(self): - data = b"\x00" + os.urandom(32) + b"\x00" - signature = self.dsa_key.sign(data) - - self.assertIsNone(self.dsa_key.public_key.verify(data, signature)) - - with self.assertRaises(_EX.InvalidSignatureException): - self.dsa_key.public_key.verify(data, signature + b"\x00") - def test_ecdsa_signature(self): data = b"\x00" + os.urandom(32) + b"\x00" signature = self.ecdsa_key.sign(data) From dfef738eb58a2c6798813603ba5cc7d1144a23d7 Mon Sep 17 00:00:00 2001 From: Lars Scheibling Date: Wed, 7 Jun 2023 13:00:17 +0000 Subject: [PATCH 07/26] Removed DSA Support --- README.md | 14 ++-- src/sshkey_tools/cert.py | 14 ++-- src/sshkey_tools/exceptions.py | 5 ++ src/sshkey_tools/fields.py | 88 ++++--------------------- src/sshkey_tools/keys.py | 116 +++++---------------------------- tests/test_certificates.py | 17 +---- 6 files changed, 47 insertions(+), 207 deletions(-) diff --git a/README.md b/README.md index 0e6ada8..1ba20ff 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ Python package for managing OpenSSH keypairs and certificates ([protocol.CERTKEYS](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)). Supported functionality includes: -# Notice +## Notice The DSA algorithm has been deprecated and is removed in pyca/cryptography 41.x, meaning **version 0.9.* of this package will be the last to support DSA keys and certificates** for SSH. If there is any demand to reintroduce DSA support, please open an issue regarding this and we'll look into it. For now, **0.9.* will be restricted to version <41.1 of the cryptography package** and **0.10 will have its DSA support removed**. We've introduced a deprecation notice in version 0.9.3. -## Background +### Background The DSA algorithm is considered deprecated and will be removed in a future version. If possible, use RSA, [(ECDSA)](https://billatnapier.medium.com/ecdsa-weakness-where-nonces-are-reused-2be63856a01a) or ED25519 as a first-hand choice. -## Notice from OpenSSH: +### Notice from OpenSSH: ``` OpenSSH 7.0 and greater similarly disable the ssh-dss (DSA) public key algorithm. It too is weak and we recommend against its use. It can be re-enabled using the HostKeyAlgorithms configuration option: sshd_config(5) HostKeyAlgorithms ``` @@ -19,7 +19,7 @@ OpenSSH 7.0 and greater similarly disable the ssh-dss (DSA) public key algorithm # Features ### SSH Keys -- Supports RSA, DSA (Note: Deprecated), ECDSA and ED25519 keys +- Supports RSA, ECDSA and ED25519 keys - Import existing keys from file, string, byte data or [pyca/cryptography](https://github.com/pyca/cryptography) class - Generate new keys - Get public key from private keys @@ -28,7 +28,7 @@ OpenSSH 7.0 and greater similarly disable the ssh-dss (DSA) public key algorithm - Generate fingerprint ### OpenSSH Certificates -- Supports RSA, DSA, ECDSA and ED25519 certificates +- Supports RSA, ECDSA and ED25519 certificates - Import existing certificates from file, string or bytes - Verify certificate signature against internal or separate public key - Create new certificates from CA private key and subject public key @@ -72,7 +72,6 @@ rm -rf docs/sshkey_tools # Import the certificate classes from sshkey_tools.keys import ( RsaPrivateKey, - DsaPrivateKey, EcdsaPrivateKey, Ed25519PrivateKey, EcdsaCurves @@ -87,7 +86,8 @@ rsa_priv = RsaPrivateKey.generate() rsa_priv = RsaPrivateKey.generate(2048) # Generate DSA keys (since SSH only supports 1024-bit keys, this is the default) -dsa_priv = DsaPrivateKey.generate() +# DEPRECATED +# dsa_priv = DsaPrivateKey.generate() # Generate ECDSA keys (The default curve is P521) ecdsa_priv = EcdsaPrivateKey.generate() diff --git a/src/sshkey_tools/cert.py b/src/sshkey_tools/cert.py index ee04421..e952d96 100644 --- a/src/sshkey_tools/cert.py +++ b/src/sshkey_tools/cert.py @@ -24,7 +24,6 @@ "ssh-rsa-cert-v01@openssh.com": ("RsaCertificate", "RsaPubkeyField"), "rsa-sha2-256-cert-v01@openssh.com": ("RsaCertificate", "RsaPubkeyField"), "rsa-sha2-512-cert-v01@openssh.com": ("RsaCertificate", "RsaPubkeyField"), - "ssh-dss-cert-v01@openssh.com": ("DsaCertificate", "DsaPubkeyField"), "ecdsa-sha2-nistp256-cert-v01@openssh.com": ( "EcdsaCertificate", "EcdsaPubkeyField", @@ -579,14 +578,11 @@ class RsaCertificate(SSHCertificate): class DsaCertificate(SSHCertificate): """The DSA Certificate class (DEPRECATED)""" - - DEFAULT_KEY_TYPE = "ssh-dss-cert-v01@openssh.com" - - def __post_init__(self): - """Display the deprecation notice""" - warnings.warn( - "SSH DSA keys and certificates are deprecated and will be removed in version 0.10 of sshkey-tools", - stacklevel=2, + + def __init__(self, *args, **kwargs): + """DEPRECATED CERTIFICATE CLASS""" + raise _EX.DeprecatedClassCalled( + "DSA certificates are deprecated and have been removed since version 0.10 of sshkey-tools" ) diff --git a/src/sshkey_tools/exceptions.py b/src/sshkey_tools/exceptions.py index c71e508..0cc691c 100644 --- a/src/sshkey_tools/exceptions.py +++ b/src/sshkey_tools/exceptions.py @@ -107,3 +107,8 @@ class InvalidClassCallException(ValueError): """ Raised when trying to instantiate a parent class """ + +class DeprecatedClassCalled(ValueError): + """ + Raised when trying to instantiate a deprecated class + """ \ No newline at end of file diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index 81fd844..4e70608 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -16,8 +16,6 @@ from . import exceptions as _EX from .keys import ( - DsaPrivateKey, - DsaPublicKey, EcdsaPrivateKey, EcdsaPublicKey, Ed25519PrivateKey, @@ -53,21 +51,18 @@ SUBJECT_PUBKEY_MAP = { RsaPublicKey: "RsaPubkeyField", - DsaPublicKey: "DsaPubkeyField", EcdsaPublicKey: "EcdsaPubkeyField", Ed25519PublicKey: "Ed25519PubkeyField", } CA_SIGNATURE_MAP = { RsaPrivateKey: "RsaSignatureField", - DsaPrivateKey: "DsaSignatureField", EcdsaPrivateKey: "EcdsaSignatureField", Ed25519PrivateKey: "Ed25519SignatureField", } SIGNATURE_TYPE_MAP = { b"rsa": "RsaSignatureField", - b"dss": "DsaSignatureField", b"ecdsa": "EcdsaSignatureField", b"ed25519": "Ed25519SignatureField", } @@ -727,7 +722,6 @@ class PubkeyTypeField(StringField): "ssh-rsa-cert-v01@openssh.com", "rsa-sha2-256-cert-v01@openssh.com", "rsa-sha2-512-cert-v01@openssh.com", - "ssh-dss-cert-v01@openssh.com", "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ecdsa-sha2-nistp521-cert-v01@openssh.com", @@ -867,11 +861,8 @@ class DsaPubkeyField(PublicKeyField): Holds the DSA Public Key for DSA Certificates """ - DEFAULT = None - DATA_TYPE = DsaPublicKey - @staticmethod - def decode(data: bytes) -> Tuple[DsaPublicKey, bytes]: + def decode(data: bytes): """ Decode the certificate field from a byte string starting with the encoded public key @@ -882,12 +873,7 @@ def decode(data: bytes) -> Tuple[DsaPublicKey, bytes]: Returns: Tuple[RsaPublicKey, bytes]: The PublicKey field and remainder of the data """ - p, data = MpIntegerField.decode(data) - q, data = MpIntegerField.decode(data) - g, data = MpIntegerField.decode(data) - y, data = MpIntegerField.decode(data) - - return DsaPublicKey.from_numbers(p=p, q=q, g=g, y=y), data + raise _EX.DeprecatedClassCalled("DSA is deprecated, use RSA or ECDSA instead") class EcdsaPubkeyField(PublicKeyField): @@ -1469,16 +1455,13 @@ class DsaSignatureField(SignatureField): Creates and contains the DSA signature from an DSA Private Key """ - DEFAULT = None - DATA_TYPE = bytes - - def __init__( - self, private_key: DsaPrivateKey = None, signature: bytes = None - ) -> None: - super().__init__(private_key, signature) + def __init__(self, *args, **kwargs) -> None: + raise _EX.DeprecatedClassCalled( + "DSA signatures are deprecated and have been removed" + ) @classmethod - def encode(cls, value: bytes): + def encode(cls, value = None): """ Encodes the signature to a byte string @@ -1488,62 +1471,15 @@ def encode(cls, value: bytes): Returns: bytes: The encoded byte string """ - cls.__validate_type__(value, True) - - r, s = decode_dss_signature(value) - - return BytestringField.encode( - StringField.encode("ssh-dss") - + BytestringField.encode(long_to_bytes(r, 20) + long_to_bytes(s, 20)) - ) + cls() @staticmethod - def decode(data: bytes) -> Tuple[bytes, bytes]: - """ - Decodes a bytestring containing a signature - - Args: - data (bytes): The bytestring starting with the Signature - - Returns: - Tuple[ bytes, bytes ]: signature, remainder of the data - """ - signature, data = BytestringField.decode(data) - - signature = BytestringField.decode(BytestringField.decode(signature)[1])[0] - r = bytes_to_long(signature[:20]) - s = bytes_to_long(signature[20:]) - - signature = encode_dss_signature(r, s) - - return signature, data + def decode(data = None): + DsaSignatureField() @classmethod - def from_decode(cls, data: bytes) -> Tuple["DsaSignatureField", bytes]: - """ - Creates a signature field class from the encoded signature - - Args: - data (bytes): The bytestring starting with the Signature - - Returns: - Tuple[ DsaSignatureField, bytes ]: signature, remainder of the data - """ - signature, data = cls.decode(data) - - return cls(private_key=None, signature=signature), data - - # pylint: disable=unused-argument - def sign(self, data: bytes, **kwargs) -> None: - """ - Signs the provided data with the provided private key - - Args: - data (bytes): The data to be signed - """ - self.value = self.private_key.sign(data) - self.is_signed = True - + def from_decode(cls, data = None): + cls() class EcdsaSignatureField(SignatureField): """ diff --git a/src/sshkey_tools/keys.py b/src/sshkey_tools/keys.py index a5fb829..7bc7d79 100644 --- a/src/sshkey_tools/keys.py +++ b/src/sshkey_tools/keys.py @@ -8,15 +8,12 @@ from typing import Union from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.backends.openssl.dsa import _DSAPrivateKey, _DSAPublicKey from cryptography.hazmat.backends.openssl.ec import ( _EllipticCurvePrivateKey, _EllipticCurvePublicKey, ) -from cryptography.hazmat.backends.openssl.ed25519 import ( - _Ed25519PrivateKey, - _Ed25519PublicKey, -) +from cryptography.hazmat.bindings import _rust as _RustBinding + from cryptography.hazmat.backends.openssl.rsa import _RSAPrivateKey, _RSAPublicKey from cryptography.hazmat.primitives import hashes as _HASHES from cryptography.hazmat.primitives import serialization as _SERIALIZATION @@ -34,17 +31,15 @@ PUBKEY_MAP = { _RSAPublicKey: "RsaPublicKey", - _DSAPublicKey: "DsaPublicKey", _EllipticCurvePublicKey: "EcdsaPublicKey", - _Ed25519PublicKey: "Ed25519PublicKey", + _RustBinding.openssl.ed25519.Ed25519PublicKey: "Ed25519PublicKey", } PRIVKEY_MAP = { _RSAPrivateKey: "RsaPrivateKey", - _DSAPrivateKey: "DsaPrivateKey", _EllipticCurvePrivateKey: "EcdsaPrivateKey", # trunk-ignore(gitleaks/generic-api-key) - _Ed25519PrivateKey: "Ed25519PrivateKey", + _RustBinding.openssl.ed25519.Ed25519PrivateKey: "Ed25519PrivateKey", } ECDSA_HASHES = { @@ -599,87 +594,30 @@ class DsaPublicKey(PublicKey): Class for holding DSA public keys """ - def __init__( - self, - key: _DSA.DSAPublicKey, - comment: Union[str, bytes] = None, - key_type: Union[str, bytes] = None, - serialized: bytes = None, - ): - super().__init__( - key=key, - comment=comment, - key_type=key_type, - public_numbers=key.public_numbers(), - serialized=serialized, + def __init__(self, key = None, comment = None, key_type = None, serialized = None): + raise _EX.DeprecatedClassCalled( + "SSH DSA keys and certificates are deprecated and are removed since version 0.10 of sshkey-tools", ) - self.parameters = key.parameters().parameter_numbers() - - warnings.warn( - "SSH DSA keys and certificates are deprecated and will be removed in version 0.10 of sshkey-tools", - stacklevel=2, - ) - + @classmethod # pylint: disable=invalid-name def from_numbers(cls, p: int, q: int, g: int, y: int) -> "DsaPublicKey": - """ - Create a DSA public key from public numbers and parameters - - Args: - p (int): P parameter, the prime modulus - q (int): Q parameter, the order of the subgroup - g (int): G parameter, the generator - y (int): The public number Y - - Returns: - DsaPublicKey: An instance of DsaPublicKey - """ - return cls( - key=_DSA.DSAPublicNumbers( - y=y, parameter_numbers=_DSA.DSAParameterNumbers(p=p, q=q, g=g) - ).public_key() - ) - - def verify(self, data: bytes, signature: bytes) -> None: - """ - Verifies a signature - - Args: - data (bytes): The data to verify - signature (bytes): The signature to verify - - Raises: - Raises an sshkey_tools.exceptions.InvalidSignatureException if the signature is invalid - """ - try: - return self.key.verify(signature, data, _HASHES.SHA1()) - except InvalidSignature: - raise _EX.InvalidSignatureException( - "The signature is invalid for the given data" - ) from InvalidSignature - + return cls() + class DsaPrivateKey(PrivateKey): """ Class for holding DSA private keys """ - def __init__(self, key: _DSA.DSAPrivateKey): - super().__init__( - key=key, - public_key=DsaPublicKey(key.public_key()), - private_numbers=key.private_numbers(), - ) - - warnings.warn( - "SSH DSA keys and certificates are deprecated and will be removed in version 0.10 of sshkey-tools", - stacklevel=2, + def __init__(self, key = None): + raise _EX.DeprecatedClassCalled( + "SSH DSA keys and certificates are deprecated and are removed since version 0.10 of sshkey-tools", ) @classmethod # pylint: disable=invalid-name,too-many-arguments - def from_numbers(cls, p: int, q: int, g: int, y: int, x: int) -> "DsaPrivateKey": + def from_numbers(cls, p, q, g, y, x): """ Creates a new DsaPrivateKey object from parameters and public/private numbers @@ -693,15 +631,8 @@ def from_numbers(cls, p: int, q: int, g: int, y: int, x: int) -> "DsaPrivateKey" Returns: _type_: _description_ """ - return cls( - key=_DSA.DSAPrivateNumbers( - public_numbers=_DSA.DSAPublicNumbers( - y=y, parameter_numbers=_DSA.DSAParameterNumbers(p=p, q=q, g=g) - ), - x=x, - ).private_key() - ) - + return cls() + @classmethod def generate(cls) -> "DsaPrivateKey": """ @@ -711,20 +642,7 @@ def generate(cls) -> "DsaPrivateKey": Returns: DsaPrivateKey: An instance of DsaPrivateKey """ - return cls.from_class(_DSA.generate_private_key(key_size=1024)) - - def sign(self, data: bytes): - """ - Signs a block of data and returns the signature - - Args: - data (bytes): Block of byte data to sign - - Returns: - bytes: The signature bytes - """ - return self.key.sign(data, _HASHES.SHA1()) - + return cls() class EcdsaPublicKey(PublicKey): """ diff --git a/tests/test_certificates.py b/tests/test_certificates.py index 682c0a3..62976a4 100644 --- a/tests/test_certificates.py +++ b/tests/test_certificates.py @@ -16,14 +16,13 @@ import src.sshkey_tools.fields as _FIELD import src.sshkey_tools.keys as _KEY -CERTIFICATE_TYPES = ["rsa", "dsa", "ecdsa", "ed25519"] +CERTIFICATE_TYPES = ["rsa", "ecdsa", "ed25519"] class TestCertificateFields(unittest.TestCase): def setUp(self): self.faker = faker.Faker() self.rsa_key = _KEY.RsaPrivateKey.generate(1024) - self.dsa_key = _KEY.DsaPrivateKey.generate() self.ecdsa_key = _KEY.EcdsaPrivateKey.generate() self.ed25519_key = _KEY.Ed25519PrivateKey.generate() @@ -232,10 +231,6 @@ def test_pubkey_type_field(self): "rsa-sha2-512-cert-v01@openssh.com", b"\x00\x00\x00!rsa-sha2-512-cert-v01@openssh.com", ), - ( - "ssh-dss-cert-v01@openssh.com", - b"\x00\x00\x00\x1cssh-dss-cert-v01@openssh.com", - ), ( "ecdsa-sha2-nistp256-cert-v01@openssh.com", b"\x00\x00\x00(ecdsa-sha2-nistp256-cert-v01@openssh.com", @@ -281,17 +276,14 @@ def test_nonce_field(self): def test_pubkey_class_assignment(self): rsa_field = _FIELD.PublicKeyField.from_object(self.rsa_key.public_key) - dsa_field = _FIELD.PublicKeyField.from_object(self.dsa_key.public_key) ecdsa_field = _FIELD.PublicKeyField.from_object(self.ecdsa_key.public_key) ed25519_field = _FIELD.PublicKeyField.from_object(self.ed25519_key.public_key) self.assertIsInstance(rsa_field, _FIELD.RsaPubkeyField) - self.assertIsInstance(dsa_field, _FIELD.DsaPubkeyField) self.assertIsInstance(ecdsa_field, _FIELD.EcdsaPubkeyField) self.assertIsInstance(ed25519_field, _FIELD.Ed25519PubkeyField) self.assertTrue(rsa_field.validate()) - self.assertTrue(dsa_field.validate()) self.assertTrue(ecdsa_field.validate()) self.assertTrue(ed25519_field.validate()) @@ -316,9 +308,6 @@ def assertPubkeyOutput(self, key_class, *opts): def test_rsa_pubkey_output(self): self.assertPubkeyOutput(_KEY.RsaPrivateKey, 1024) - def test_dsa_pubkey_output(self): - self.assertPubkeyOutput(_KEY.DsaPrivateKey) - def test_ecdsa_pubkey_output(self): self.assertPubkeyOutput(_KEY.EcdsaPrivateKey) @@ -551,12 +540,10 @@ def setUp(self): self.faker = faker.Faker() self.rsa_ca = _KEY.RsaPrivateKey.generate(1024) - self.dsa_ca = _KEY.DsaPrivateKey.generate() self.ecdsa_ca = _KEY.EcdsaPrivateKey.generate() self.ed25519_ca = _KEY.Ed25519PrivateKey.generate() self.rsa_user = _KEY.RsaPrivateKey.generate(1024).public_key - self.dsa_user = _KEY.DsaPrivateKey.generate().public_key self.ecdsa_user = _KEY.EcdsaPrivateKey.generate().public_key self.ed25519_user = _KEY.Ed25519PrivateKey.generate().public_key @@ -581,12 +568,10 @@ def tearDown(self): def test_cert_type_assignment(self): rsa_cert = _CERT.SSHCertificate.create(self.rsa_user) - dsa_cert = _CERT.SSHCertificate.create(self.dsa_user) ecdsa_cert = _CERT.SSHCertificate.create(self.ecdsa_user) ed25519_cert = _CERT.SSHCertificate.create(self.ed25519_user) self.assertIsInstance(rsa_cert, _CERT.RsaCertificate) - self.assertIsInstance(dsa_cert, _CERT.DsaCertificate) self.assertIsInstance(ecdsa_cert, _CERT.EcdsaCertificate) self.assertIsInstance(ed25519_cert, _CERT.Ed25519Certificate) From 81708b349f10faffcd6dd1c4f85e4d44bd9b701a Mon Sep 17 00:00:00 2001 From: Lars Scheibling Date: Wed, 7 Jun 2023 13:30:09 +0000 Subject: [PATCH 08/26] Fixed validation for string values for the datetime fields --- src/sshkey_tools/fields.py | 49 +++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index 4e70608..5847fae 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -465,16 +465,29 @@ def encode(cls, value: Union[datetime, int, str]) -> bytes: cls.__validate_type__(value, True) if isinstance(value, str): - if value == "forever": - return Integer64Field.encode(MAX_INT64 - 1) - - value = int((datetime.now() + str_to_time_delta(value)).timestamp()) + value = cls.parse_string_value(value) if isinstance(value, datetime): value = int(value.timestamp()) return Integer64Field.encode(value) + @staticmethod + def parse_string_value(value: str) -> int: + """ + Parses a string value into an integer timestamp + + Args: + value (str): String value to parse + + Returns: + int: Integer timestamp + """ + if value == "forever": + return MAX_INT64 - 1 + + return int((datetime.now() + str_to_time_delta(value)).timestamp()) + @staticmethod def decode(data: bytes) -> datetime: """Decodes a datetime object from a block of bytes @@ -497,7 +510,12 @@ def __validate_value__(self) -> Union[bool, Exception]: f"{self.get_name()} Could not validate value, invalid type" ) - check = self.value if isinstance(self.value, int) else self.value.timestamp() + check = self.value + if isinstance(check, str): + check = self.parse_string_value(check) + + if isinstance(check, datetime): + check = int(check.timestamp()) if check < MAX_INT64: return True @@ -1025,7 +1043,7 @@ class ValidAfterField(DateTimeField): """ DEFAULT = datetime.now() - DATA_TYPE = (datetime, int) + DATA_TYPE = (datetime, int, str) class ValidBeforeField(DateTimeField): @@ -1035,7 +1053,7 @@ class ValidBeforeField(DateTimeField): """ DEFAULT = datetime.now() + timedelta(minutes=10) - DATA_TYPE = (datetime, int) + DATA_TYPE = (datetime, int, str) def __validate_value__(self) -> Union[bool, Exception]: """ @@ -1050,13 +1068,16 @@ def __validate_value__(self) -> Union[bool, Exception]: ) super().__validate_value__() - check = ( - self.value - if isinstance(self.value, datetime) - else datetime.fromtimestamp(self.value) - ) - - if check < datetime.now(): + + check = self.value + if isinstance(check, str): + check = self.parse_string_value(check) + + if isinstance(check, datetime): + check = int(check.timestamp()) + + + if check < int(datetime.now().timstamp()): return _EX.InvalidCertificateFieldException( "The certificate validity period is invalid" + " (expected a future datetime object or timestamp)" From bcd27b2f807da1ccfede3d8c2044a73db1a5dd54 Mon Sep 17 00:00:00 2001 From: Lars Scheibling Date: Wed, 7 Jun 2023 13:33:11 +0000 Subject: [PATCH 09/26] Fixed misspelling of timestamp --- src/sshkey_tools/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index 5847fae..312ca25 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -1077,7 +1077,7 @@ def __validate_value__(self) -> Union[bool, Exception]: check = int(check.timestamp()) - if check < int(datetime.now().timstamp()): + if check < int(datetime.now().timestamp()): return _EX.InvalidCertificateFieldException( "The certificate validity period is invalid" + " (expected a future datetime object or timestamp)" From c985e5490d4eddc0cde9bdd5269525240558bfd3 Mon Sep 17 00:00:00 2001 From: Lars Scheibling Date: Wed, 7 Jun 2023 15:05:35 +0000 Subject: [PATCH 10/26] Added "always" option to datetime fields --- src/sshkey_tools/fields.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index 312ca25..27fa49d 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -486,6 +486,9 @@ def parse_string_value(value: str) -> int: if value == "forever": return MAX_INT64 - 1 + if value == "always": + return 1 + return int((datetime.now() + str_to_time_delta(value)).timestamp()) @staticmethod From 212e0ba3231639587bc6bcaef63f05dbb9e24a58 Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Tue, 3 Oct 2023 09:49:00 +0000 Subject: [PATCH 11/26] Pinned dependency versions Added devcontainer config Added pyenv config --- .devcontainer/devcontainer.json | 13 + Pipfile | 23 ++ Pipfile.lock | 554 ++++++++++++++++++++++++++++++++ dev.Dockerfile | 7 + requirements.txt | 12 +- src/sshkey_tools/__version__.py | 2 +- 6 files changed, 604 insertions(+), 7 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 dev.Dockerfile diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..42c2e00 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,13 @@ +{ + "build": { + "dockerfile": "dev.Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": [ + "scheiblingco.code-pypack", + "scheiblingco.code-cmd-repeat" + ] + } + } +} \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..5403007 --- /dev/null +++ b/Pipfile @@ -0,0 +1,23 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +click = ">=7.1" +cryptography = ">=41.0" +bcrypt = ">=4.0" +enum34 = ">=1.1" +prettytable = ">=3.1" +pytimeparse2 = ">=1.4" + +[dev-packages] +paramiko = "*" +coverage = "*" +black = "*" +pytest-cov = "*" +faker = "*" +cprint = "*" + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..6a7ef7e --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,554 @@ +{ + "_meta": { + "hash": { + "sha256": "b26c494b3bc430be6555ae1d168b5c07771a6c38a5db127bcc14516abc07c91f" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "bcrypt": { + "hashes": [ + "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535", + "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0", + "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410", + "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd", + "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665", + "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab", + "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71", + "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215", + "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b", + "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda", + "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9", + "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a", + "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344", + "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f", + "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d", + "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c", + "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c", + "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2", + "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d", + "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e", + "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==4.0.1" + }, + "cffi": { + "hashes": [ + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "python_version >= '3.8'", + "version": "==1.16.0" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "cryptography": { + "hashes": [ + "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", + "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", + "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", + "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", + "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", + "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", + "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", + "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", + "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", + "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", + "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", + "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", + "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", + "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", + "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", + "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", + "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", + "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", + "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", + "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", + "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", + "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", + "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==41.0.4" + }, + "enum34": { + "hashes": [ + "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53", + "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328", + "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248" + ], + "index": "pypi", + "version": "==1.1.10" + }, + "prettytable": { + "hashes": [ + "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", + "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.9.0" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "pytimeparse2": { + "hashes": [ + "sha256:98668cdcba4890e1789e432e8ea0059ccf72402f13f5d52be15bdfaeb3a8b253", + "sha256:a162ea6a7707fd0bb82dd99556efb783935f51885c8bdced0fce3fffe85ab002" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==1.7.1" + }, + "wcwidth": { + "hashes": [ + "sha256:77f719e01648ed600dfa5402c347481c0992263b81a027344f3e1ba25493a704", + "sha256:8705c569999ffbb4f6a87c6d1b80f324bd6db952f5eb0b95bc07517f4c1813d4" + ], + "version": "==0.2.8" + } + }, + "develop": { + "bcrypt": { + "hashes": [ + "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535", + "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0", + "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410", + "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd", + "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665", + "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab", + "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71", + "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215", + "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b", + "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda", + "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9", + "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a", + "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344", + "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f", + "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d", + "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c", + "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c", + "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2", + "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d", + "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e", + "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==4.0.1" + }, + "black": { + "hashes": [ + "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f", + "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7", + "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100", + "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573", + "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d", + "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f", + "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9", + "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300", + "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948", + "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325", + "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9", + "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71", + "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186", + "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f", + "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe", + "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855", + "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80", + "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393", + "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c", + "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204", + "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377", + "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==23.9.1" + }, + "cffi": { + "hashes": [ + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "python_version >= '3.8'", + "version": "==1.16.0" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "coverage": { + "hashes": [ + "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1", + "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63", + "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9", + "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312", + "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3", + "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb", + "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25", + "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92", + "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda", + "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148", + "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6", + "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216", + "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a", + "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640", + "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836", + "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c", + "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f", + "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2", + "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901", + "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed", + "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a", + "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074", + "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc", + "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84", + "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083", + "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f", + "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c", + "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c", + "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637", + "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2", + "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82", + "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f", + "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce", + "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef", + "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f", + "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611", + "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c", + "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76", + "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9", + "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce", + "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9", + "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf", + "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf", + "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9", + "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6", + "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2", + "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a", + "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a", + "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf", + "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738", + "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a", + "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==7.3.2" + }, + "cprint": { + "hashes": [ + "sha256:83469274db24939b27a9b3a03d5298fec6f07408c3175dc0fc21d5ba206f51c8" + ], + "index": "pypi", + "version": "==1.2.2" + }, + "cryptography": { + "hashes": [ + "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", + "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", + "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", + "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", + "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", + "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", + "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", + "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", + "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", + "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", + "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", + "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", + "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", + "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", + "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", + "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", + "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", + "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", + "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", + "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", + "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", + "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", + "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==41.0.4" + }, + "exceptiongroup": { + "hashes": [ + "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", + "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3" + ], + "markers": "python_version < '3.11'", + "version": "==1.1.3" + }, + "faker": { + "hashes": [ + "sha256:8fba91068dc26e3159c1ac9f22444a2338704b0991d86605322e454bda420092", + "sha256:d5d5953556b0fb428a46019e03fc2d40eab2980135ddef5a9eb3d054947fdf83" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==19.6.2" + }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "paramiko": { + "hashes": [ + "sha256:6a3777a961ac86dbef375c5f5b8d50014a1a96d0fd7f054a43bc880134b0ff77", + "sha256:b7bc5340a43de4287bbe22fe6de728aa2c22468b2a849615498dd944c2f275eb" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==3.3.1" + }, + "pathspec": { + "hashes": [ + "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", + "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3" + ], + "markers": "python_version >= '3.7'", + "version": "==0.11.2" + }, + "platformdirs": { + "hashes": [ + "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", + "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" + ], + "markers": "python_version >= '3.7'", + "version": "==3.11.0" + }, + "pluggy": { + "hashes": [ + "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", + "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + ], + "markers": "python_version >= '3.8'", + "version": "==1.3.0" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "pynacl": { + "hashes": [ + "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", + "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", + "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", + "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", + "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", + "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", + "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", + "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", + "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", + "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543" + ], + "markers": "python_version >= '3.6'", + "version": "==1.5.0" + }, + "pytest": { + "hashes": [ + "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002", + "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069" + ], + "markers": "python_version >= '3.7'", + "version": "==7.4.2" + }, + "pytest-cov": { + "hashes": [ + "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", + "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.1.0" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + }, + "typing-extensions": { + "hashes": [ + "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", + "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" + ], + "markers": "python_version < '3.11'", + "version": "==4.8.0" + } + } +} diff --git a/dev.Dockerfile b/dev.Dockerfile new file mode 100644 index 0000000..fb42284 --- /dev/null +++ b/dev.Dockerfile @@ -0,0 +1,7 @@ +FROM mcr.microsoft.com/devcontainers/universal:2 + +RUN curl https://pyenv.run | bash \ + && echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc \ + && echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc \ + && echo 'eval "$(pyenv init -)"' >> ~/.bashrc + diff --git a/requirements.txt b/requirements.txt index d7c755c..44996ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -click -cryptography -bcrypt -enum34 -PrettyTable -pytimeparse2 \ No newline at end of file +click>=7.1 +cryptography>=41.0 +bcrypt>=4.0 +enum34>=1.1 +PrettyTable>=3.1 +pytimeparse2>=1.4 \ No newline at end of file diff --git a/src/sshkey_tools/__version__.py b/src/sshkey_tools/__version__.py index 593c63e..f2c883d 100644 --- a/src/sshkey_tools/__version__.py +++ b/src/sshkey_tools/__version__.py @@ -9,7 +9,7 @@ # The version and build number # Without specifying a unique number, you cannot overwrite packages in the PyPi repo -__version__ = os.getenv("RELEASE_NAME", "0.9-dev" + os.getenv("GITHUB_RUN_ID", "")) +__version__ = os.getenv("RELEASE_NAME", "0.10.2-dev" + os.getenv("GITHUB_RUN_ID", "")) # Author and license information __author__ = "Lars Scheibling" From 874134d4d978f10d627adb8673b12996b88f03f4 Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:05:57 +0000 Subject: [PATCH 12/26] Linting, switched back to pylint_report --- .github/workflows/linting.yml | 5 +- .pylintrc | 6 +- Pipfile | 1 + Pipfile.lock | 261 ++++++++++++++++++++++++++++++++- src/sshkey_tools/cert.py | 7 +- src/sshkey_tools/exceptions.py | 3 +- src/sshkey_tools/fields.py | 23 +-- src/sshkey_tools/keys.py | 25 ++-- 8 files changed, 300 insertions(+), 31 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 5a22f12..499d5cd 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -23,12 +23,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pylint pylint-report pylint-json2html + pip install -r requirements-test.txt - name: Run tests run: | - pylint src/sshkey_tools | tee | pylint-json2html -o report.html + python3 -m pylint src/sshkey_tools | tee | pylint_report --output=report.html - name: Upload report uses: actions/upload-artifact@v1 diff --git a/.pylintrc b/.pylintrc index 7321f5e..c6120ab 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,2 +1,6 @@ +[MASTER] +load-plugins=pylint_report +extension-pkg-allow-list=cryptography.hazmat.bindings._rust + [REPORTS] -output-format=json \ No newline at end of file +output-format=pylint_report.CustomJsonReporter \ No newline at end of file diff --git a/Pipfile b/Pipfile index 5403007..d5b9665 100644 --- a/Pipfile +++ b/Pipfile @@ -12,6 +12,7 @@ prettytable = ">=3.1" pytimeparse2 = ">=1.4" [dev-packages] +pylint-report = "*" paramiko = "*" coverage = "*" black = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 6a7ef7e..8af1ae8 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b26c494b3bc430be6555ae1d168b5c07771a6c38a5db127bcc14516abc07c91f" + "sha256": "bd496f9f21bdc981b9691aef03737b797dcf6ed135ff3fcb433368265552abf6" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,22 @@ ] }, "default": { + "argcomplete": { + "hashes": [ + "sha256:d5d1e5efd41435260b8f85673b74ea2e883affcbec9f4230c582689e8e78251b", + "sha256:d97c036d12a752d1079f190bc1521c545b941fda89ad85d15afa909b4d1b9a99" + ], + "markers": "python_version >= '3.6'", + "version": "==3.1.2" + }, + "astroid": { + "hashes": [ + "sha256:1defdbca052635dd29657ea674edfc45e4b5be9cd53630c5b084fcfed94344a8", + "sha256:f2510e7fdcd6cfda4ec50014726d4857abf79acfc010084ce8c26091913f1b25" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==3.0.0" + }, "bcrypt": { "hashes": [ "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535", @@ -141,6 +157,14 @@ "markers": "python_version >= '3.7'", "version": "==41.0.4" }, + "dill": { + "hashes": [ + "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", + "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" + ], + "markers": "python_version < '3.11'", + "version": "==0.3.7" + }, "enum34": { "hashes": [ "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53", @@ -150,6 +174,175 @@ "index": "pypi", "version": "==1.1.10" }, + "isort": { + "hashes": [ + "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", + "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==5.12.0" + }, + "jinja2": { + "hashes": [ + "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", + "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.2" + }, + "markupsafe": { + "hashes": [ + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", + "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", + "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.3" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "numpy": { + "hashes": [ + "sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2", + "sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292", + "sha256:0792824ce2f7ea0c82ed2e4fecc29bb86bee0567a080dacaf2e0a01fe7654369", + "sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91", + "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388", + "sha256:186ba67fad3c60dbe8a3abff3b67a91351100f2661c8e2a80364ae6279720299", + "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd", + "sha256:436c8e9a4bdeeee84e3e59614d38c3dbd3235838a877af8c211cfcac8a80b8d3", + "sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2", + "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69", + "sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68", + "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148", + "sha256:5671338034b820c8d58c81ad1dafc0ed5a00771a82fccc71d6438df00302094b", + "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a", + "sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be", + "sha256:7d484292eaeb3e84a51432a94f53578689ffdea3f90e10c8b203a99be5af57d8", + "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505", + "sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c", + "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208", + "sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8", + "sha256:b44e6a09afc12952a7d2a58ca0a2429ee0d49a4f89d83a0a11052da696440e49", + "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95", + "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229", + "sha256:c2e698cb0c6dda9372ea98a0344245ee65bdc1c9dd939cceed6bb91256837896", + "sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f", + "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c", + "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb", + "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99", + "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112", + "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581", + "sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd", + "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf" + ], + "markers": "python_version < '3.10'", + "version": "==1.26.0" + }, + "pandas": { + "hashes": [ + "sha256:0183cb04a057cc38fde5244909fca9826d5d57c4a5b7390c0cc3fa7acd9fa883", + "sha256:1fc87eac0541a7d24648a001d553406f4256e744d92df1df8ebe41829a915028", + "sha256:220b98d15cee0b2cd839a6358bd1f273d0356bf964c1a1aeb32d47db0215488b", + "sha256:2552bffc808641c6eb471e55aa6899fa002ac94e4eebfa9ec058649122db5824", + "sha256:315e19a3e5c2ab47a67467fc0362cb36c7c60a93b6457f675d7d9615edad2ebe", + "sha256:344021ed3e639e017b452aa8f5f6bf38a8806f5852e217a7594417fb9bbfa00e", + "sha256:375262829c8c700c3e7cbb336810b94367b9c4889818bbd910d0ecb4e45dc261", + "sha256:457d8c3d42314ff47cc2d6c54f8fc0d23954b47977b2caed09cd9635cb75388b", + "sha256:4aed257c7484d01c9a194d9a94758b37d3d751849c05a0050c087a358c41ad1f", + "sha256:530948945e7b6c95e6fa7aa4be2be25764af53fba93fe76d912e35d1c9ee46f5", + "sha256:5ae7e989f12628f41e804847a8cc2943d362440132919a69429d4dea1f164da0", + "sha256:71f510b0efe1629bf2f7c0eadb1ff0b9cf611e87b73cd017e6b7d6adb40e2b3a", + "sha256:73f219fdc1777cf3c45fde7f0708732ec6950dfc598afc50588d0d285fddaefc", + "sha256:8092a368d3eb7116e270525329a3e5c15ae796ccdf7ccb17839a73b4f5084a39", + "sha256:82ae615826da838a8e5d4d630eb70c993ab8636f0eff13cb28aafc4291b632b5", + "sha256:9608000a5a45f663be6af5c70c3cbe634fa19243e720eb380c0d378666bc7702", + "sha256:a40dd1e9f22e01e66ed534d6a965eb99546b41d4d52dbdb66565608fde48203f", + "sha256:b4f5a82afa4f1ff482ab8ded2ae8a453a2cdfde2001567b3ca24a4c5c5ca0db3", + "sha256:c009a92e81ce836212ce7aa98b219db7961a8b95999b97af566b8dc8c33e9519", + "sha256:c218796d59d5abd8780170c937b812c9637e84c32f8271bbf9845970f8c1351f", + "sha256:cc3cd122bea268998b79adebbb8343b735a5511ec14efb70a39e7acbc11ccbdc", + "sha256:d0d8fd58df5d17ddb8c72a5075d87cd80d71b542571b5f78178fb067fa4e9c72", + "sha256:e18bc3764cbb5e118be139b3b611bc3fbc5d3be42a7e827d1096f46087b395eb", + "sha256:e2b83abd292194f350bb04e188f9379d36b8dfac24dd445d5c87575f3beaf789", + "sha256:e7469271497960b6a781eaa930cba8af400dd59b62ec9ca2f4d31a19f2f91090", + "sha256:e9dbacd22555c2d47f262ef96bb4e30880e5956169741400af8b306bbb24a273", + "sha256:f6257b314fc14958f8122779e5a1557517b0f8e500cfb2bd53fa1f75a8ad0af2" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.2" + }, + "platformdirs": { + "hashes": [ + "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", + "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" + ], + "markers": "python_version >= '3.7'", + "version": "==3.11.0" + }, "prettytable": { "hashes": [ "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", @@ -166,6 +359,31 @@ ], "version": "==2.21" }, + "pylint": { + "hashes": [ + "sha256:21da8ed1294f88d66c82eb3e624a0993291613548bb17fbccaa220c31c41293b", + "sha256:d22816c963816d7810b87afe0bdf5c80009e1078ecbb9c8f2e2a24d4430039b1" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==3.0.0" + }, + "pylint-report": { + "hashes": [ + "sha256:cd13c8500b1eac95fe6523da2c53f8a10fd63514f46bcf36a6b55b15c0efaecd", + "sha256:ff81d52ea818258d5539805dff1d409f82464b958c4dd30d2fa2c4bf5a6c3d62" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.4.0" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, "pytimeparse2": { "hashes": [ "sha256:98668cdcba4890e1789e432e8ea0059ccf72402f13f5d52be15bdfaeb3a8b253", @@ -175,6 +393,45 @@ "markers": "python_version >= '3.6'", "version": "==1.7.1" }, + "pytz": { + "hashes": [ + "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", + "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" + ], + "version": "==2023.3.post1" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + }, + "tomlkit": { + "hashes": [ + "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", + "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899" + ], + "markers": "python_version >= '3.7'", + "version": "==0.12.1" + }, + "typing-extensions": { + "hashes": [ + "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", + "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" + ], + "markers": "python_version < '3.10'", + "version": "==4.8.0" + }, "wcwidth": { "hashes": [ "sha256:77f719e01648ed600dfa5402c347481c0992263b81a027344f3e1ba25493a704", @@ -547,7 +804,7 @@ "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" ], - "markers": "python_version < '3.11'", + "markers": "python_version < '3.10'", "version": "==4.8.0" } } diff --git a/src/sshkey_tools/cert.py b/src/sshkey_tools/cert.py index e952d96..dfda6d3 100644 --- a/src/sshkey_tools/cert.py +++ b/src/sshkey_tools/cert.py @@ -8,7 +8,6 @@ _EX.NoPrivateKeyException: The certificate contains no private key _EX.NotSignedException: The certificate is not signed and cannot be exported """ -import warnings from base64 import b64decode, b64encode from dataclasses import dataclass from typing import Tuple, Union @@ -578,11 +577,13 @@ class RsaCertificate(SSHCertificate): class DsaCertificate(SSHCertificate): """The DSA Certificate class (DEPRECATED)""" - + + # pylint: disable=super-init-not-called def __init__(self, *args, **kwargs): """DEPRECATED CERTIFICATE CLASS""" raise _EX.DeprecatedClassCalled( - "DSA certificates are deprecated and have been removed since version 0.10 of sshkey-tools" + "DSA certificates are deprecated and have been removed" + "since version 0.10 of sshkey-tools" ) diff --git a/src/sshkey_tools/exceptions.py b/src/sshkey_tools/exceptions.py index 0cc691c..7c8d336 100644 --- a/src/sshkey_tools/exceptions.py +++ b/src/sshkey_tools/exceptions.py @@ -108,7 +108,8 @@ class InvalidClassCallException(ValueError): Raised when trying to instantiate a parent class """ + class DeprecatedClassCalled(ValueError): """ Raised when trying to instantiate a deprecated class - """ \ No newline at end of file + """ diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index 27fa49d..638c6d5 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -485,10 +485,10 @@ def parse_string_value(value: str) -> int: """ if value == "forever": return MAX_INT64 - 1 - + if value == "always": return 1 - + return int((datetime.now() + str_to_time_delta(value)).timestamp()) @staticmethod @@ -516,7 +516,7 @@ def __validate_value__(self) -> Union[bool, Exception]: check = self.value if isinstance(check, str): check = self.parse_string_value(check) - + if isinstance(check, datetime): check = int(check.timestamp()) @@ -1071,15 +1071,14 @@ def __validate_value__(self) -> Union[bool, Exception]: ) super().__validate_value__() - + check = self.value if isinstance(check, str): check = self.parse_string_value(check) - + if isinstance(check, datetime): check = int(check.timestamp()) - - + if check < int(datetime.now().timestamp()): return _EX.InvalidCertificateFieldException( "The certificate validity period is invalid" @@ -1479,13 +1478,14 @@ class DsaSignatureField(SignatureField): Creates and contains the DSA signature from an DSA Private Key """ + # pylint: disable=super-init-not-called def __init__(self, *args, **kwargs) -> None: raise _EX.DeprecatedClassCalled( "DSA signatures are deprecated and have been removed" ) @classmethod - def encode(cls, value = None): + def encode(cls, value=None): """ Encodes the signature to a byte string @@ -1498,13 +1498,14 @@ def encode(cls, value = None): cls() @staticmethod - def decode(data = None): - DsaSignatureField() + def decode(data=None): + DsaSignatureField() @classmethod - def from_decode(cls, data = None): + def from_decode(cls, data=None): cls() + class EcdsaSignatureField(SignatureField): """ Creates and contains the ECDSA signature from an ECDSA Private Key diff --git a/src/sshkey_tools/keys.py b/src/sshkey_tools/keys.py index 7bc7d79..e3541a4 100644 --- a/src/sshkey_tools/keys.py +++ b/src/sshkey_tools/keys.py @@ -1,7 +1,6 @@ """ Classes for handling SSH public/private keys """ -import warnings from base64 import b64decode from enum import Enum from struct import unpack @@ -594,29 +593,34 @@ class DsaPublicKey(PublicKey): Class for holding DSA public keys """ - def __init__(self, key = None, comment = None, key_type = None, serialized = None): + # pylint: disable=super-init-not-called + def __init__(self, key=None, comment=None, key_type=None, serialized=None): raise _EX.DeprecatedClassCalled( - "SSH DSA keys and certificates are deprecated and are removed since version 0.10 of sshkey-tools", + "SSH DSA keys and certificates are deprecated " + "and are removed since version 0.10 of sshkey-tools", ) - + @classmethod - # pylint: disable=invalid-name + # pylint: disable=invalid-name,unused-argument def from_numbers(cls, p: int, q: int, g: int, y: int) -> "DsaPublicKey": + """Deprecated""" return cls() - + class DsaPrivateKey(PrivateKey): """ Class for holding DSA private keys """ - def __init__(self, key = None): + # pylint: disable=super-init-not-called + def __init__(self, key=None): raise _EX.DeprecatedClassCalled( - "SSH DSA keys and certificates are deprecated and are removed since version 0.10 of sshkey-tools", + "SSH DSA keys and certificates are deprecated " + "and are removed since version 0.10 of sshkey-tools", ) @classmethod - # pylint: disable=invalid-name,too-many-arguments + # pylint: disable=invalid-name,too-many-arguments,unused-argument def from_numbers(cls, p, q, g, y, x): """ Creates a new DsaPrivateKey object from parameters and public/private numbers @@ -632,7 +636,7 @@ def from_numbers(cls, p, q, g, y, x): _type_: _description_ """ return cls() - + @classmethod def generate(cls) -> "DsaPrivateKey": """ @@ -644,6 +648,7 @@ def generate(cls) -> "DsaPrivateKey": """ return cls() + class EcdsaPublicKey(PublicKey): """ Class for holding ECDSA public keys From acd9bfca8faee3dffba51a2468552f88b06751a2 Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:07:41 +0000 Subject: [PATCH 13/26] Added pylint --- Pipfile | 1 + Pipfile.lock | 479 +++++++++++++++++++----------------------- requirements-test.txt | 4 +- 3 files changed, 224 insertions(+), 260 deletions(-) diff --git a/Pipfile b/Pipfile index d5b9665..92fb067 100644 --- a/Pipfile +++ b/Pipfile @@ -19,6 +19,7 @@ black = "*" pytest-cov = "*" faker = "*" cprint = "*" +pylint = "*" [requires] python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock index 8af1ae8..f5e735f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "bd496f9f21bdc981b9691aef03737b797dcf6ed135ff3fcb433368265552abf6" + "sha256": "3e9ffc836ed7f6927d399ab3e7be4c355934ccac3df59207aad55166be37cedd" }, "pipfile-spec": 6, "requires": { @@ -16,22 +16,6 @@ ] }, "default": { - "argcomplete": { - "hashes": [ - "sha256:d5d1e5efd41435260b8f85673b74ea2e883affcbec9f4230c582689e8e78251b", - "sha256:d97c036d12a752d1079f190bc1521c545b941fda89ad85d15afa909b4d1b9a99" - ], - "markers": "python_version >= '3.6'", - "version": "==3.1.2" - }, - "astroid": { - "hashes": [ - "sha256:1defdbca052635dd29657ea674edfc45e4b5be9cd53630c5b084fcfed94344a8", - "sha256:f2510e7fdcd6cfda4ec50014726d4857abf79acfc010084ce8c26091913f1b25" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==3.0.0" - }, "bcrypt": { "hashes": [ "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535", @@ -157,14 +141,6 @@ "markers": "python_version >= '3.7'", "version": "==41.0.4" }, - "dill": { - "hashes": [ - "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", - "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" - ], - "markers": "python_version < '3.11'", - "version": "==0.3.7" - }, "enum34": { "hashes": [ "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53", @@ -174,175 +150,6 @@ "index": "pypi", "version": "==1.1.10" }, - "isort": { - "hashes": [ - "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", - "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==5.12.0" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "numpy": { - "hashes": [ - "sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2", - "sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292", - "sha256:0792824ce2f7ea0c82ed2e4fecc29bb86bee0567a080dacaf2e0a01fe7654369", - "sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91", - "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388", - "sha256:186ba67fad3c60dbe8a3abff3b67a91351100f2661c8e2a80364ae6279720299", - "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd", - "sha256:436c8e9a4bdeeee84e3e59614d38c3dbd3235838a877af8c211cfcac8a80b8d3", - "sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2", - "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69", - "sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68", - "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148", - "sha256:5671338034b820c8d58c81ad1dafc0ed5a00771a82fccc71d6438df00302094b", - "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a", - "sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be", - "sha256:7d484292eaeb3e84a51432a94f53578689ffdea3f90e10c8b203a99be5af57d8", - "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505", - "sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c", - "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208", - "sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8", - "sha256:b44e6a09afc12952a7d2a58ca0a2429ee0d49a4f89d83a0a11052da696440e49", - "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95", - "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229", - "sha256:c2e698cb0c6dda9372ea98a0344245ee65bdc1c9dd939cceed6bb91256837896", - "sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f", - "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c", - "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb", - "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99", - "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112", - "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581", - "sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd", - "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf" - ], - "markers": "python_version < '3.10'", - "version": "==1.26.0" - }, - "pandas": { - "hashes": [ - "sha256:0183cb04a057cc38fde5244909fca9826d5d57c4a5b7390c0cc3fa7acd9fa883", - "sha256:1fc87eac0541a7d24648a001d553406f4256e744d92df1df8ebe41829a915028", - "sha256:220b98d15cee0b2cd839a6358bd1f273d0356bf964c1a1aeb32d47db0215488b", - "sha256:2552bffc808641c6eb471e55aa6899fa002ac94e4eebfa9ec058649122db5824", - "sha256:315e19a3e5c2ab47a67467fc0362cb36c7c60a93b6457f675d7d9615edad2ebe", - "sha256:344021ed3e639e017b452aa8f5f6bf38a8806f5852e217a7594417fb9bbfa00e", - "sha256:375262829c8c700c3e7cbb336810b94367b9c4889818bbd910d0ecb4e45dc261", - "sha256:457d8c3d42314ff47cc2d6c54f8fc0d23954b47977b2caed09cd9635cb75388b", - "sha256:4aed257c7484d01c9a194d9a94758b37d3d751849c05a0050c087a358c41ad1f", - "sha256:530948945e7b6c95e6fa7aa4be2be25764af53fba93fe76d912e35d1c9ee46f5", - "sha256:5ae7e989f12628f41e804847a8cc2943d362440132919a69429d4dea1f164da0", - "sha256:71f510b0efe1629bf2f7c0eadb1ff0b9cf611e87b73cd017e6b7d6adb40e2b3a", - "sha256:73f219fdc1777cf3c45fde7f0708732ec6950dfc598afc50588d0d285fddaefc", - "sha256:8092a368d3eb7116e270525329a3e5c15ae796ccdf7ccb17839a73b4f5084a39", - "sha256:82ae615826da838a8e5d4d630eb70c993ab8636f0eff13cb28aafc4291b632b5", - "sha256:9608000a5a45f663be6af5c70c3cbe634fa19243e720eb380c0d378666bc7702", - "sha256:a40dd1e9f22e01e66ed534d6a965eb99546b41d4d52dbdb66565608fde48203f", - "sha256:b4f5a82afa4f1ff482ab8ded2ae8a453a2cdfde2001567b3ca24a4c5c5ca0db3", - "sha256:c009a92e81ce836212ce7aa98b219db7961a8b95999b97af566b8dc8c33e9519", - "sha256:c218796d59d5abd8780170c937b812c9637e84c32f8271bbf9845970f8c1351f", - "sha256:cc3cd122bea268998b79adebbb8343b735a5511ec14efb70a39e7acbc11ccbdc", - "sha256:d0d8fd58df5d17ddb8c72a5075d87cd80d71b542571b5f78178fb067fa4e9c72", - "sha256:e18bc3764cbb5e118be139b3b611bc3fbc5d3be42a7e827d1096f46087b395eb", - "sha256:e2b83abd292194f350bb04e188f9379d36b8dfac24dd445d5c87575f3beaf789", - "sha256:e7469271497960b6a781eaa930cba8af400dd59b62ec9ca2f4d31a19f2f91090", - "sha256:e9dbacd22555c2d47f262ef96bb4e30880e5956169741400af8b306bbb24a273", - "sha256:f6257b314fc14958f8122779e5a1557517b0f8e500cfb2bd53fa1f75a8ad0af2" - ], - "markers": "python_version >= '3.8'", - "version": "==1.5.2" - }, - "platformdirs": { - "hashes": [ - "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", - "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" - ], - "markers": "python_version >= '3.7'", - "version": "==3.11.0" - }, "prettytable": { "hashes": [ "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", @@ -359,31 +166,6 @@ ], "version": "==2.21" }, - "pylint": { - "hashes": [ - "sha256:21da8ed1294f88d66c82eb3e624a0993291613548bb17fbccaa220c31c41293b", - "sha256:d22816c963816d7810b87afe0bdf5c80009e1078ecbb9c8f2e2a24d4430039b1" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==3.0.0" - }, - "pylint-report": { - "hashes": [ - "sha256:cd13c8500b1eac95fe6523da2c53f8a10fd63514f46bcf36a6b55b15c0efaecd", - "sha256:ff81d52ea818258d5539805dff1d409f82464b958c4dd30d2fa2c4bf5a6c3d62" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.4.0" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, "pytimeparse2": { "hashes": [ "sha256:98668cdcba4890e1789e432e8ea0059ccf72402f13f5d52be15bdfaeb3a8b253", @@ -393,45 +175,6 @@ "markers": "python_version >= '3.6'", "version": "==1.7.1" }, - "pytz": { - "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" - ], - "version": "==2023.3.post1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "tomlkit": { - "hashes": [ - "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", - "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.1" - }, - "typing-extensions": { - "hashes": [ - "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", - "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" - ], - "markers": "python_version < '3.10'", - "version": "==4.8.0" - }, "wcwidth": { "hashes": [ "sha256:77f719e01648ed600dfa5402c347481c0992263b81a027344f3e1ba25493a704", @@ -441,6 +184,22 @@ } }, "develop": { + "argcomplete": { + "hashes": [ + "sha256:d5d1e5efd41435260b8f85673b74ea2e883affcbec9f4230c582689e8e78251b", + "sha256:d97c036d12a752d1079f190bc1521c545b941fda89ad85d15afa909b4d1b9a99" + ], + "markers": "python_version >= '3.6'", + "version": "==3.1.2" + }, + "astroid": { + "hashes": [ + "sha256:1defdbca052635dd29657ea674edfc45e4b5be9cd53630c5b084fcfed94344a8", + "sha256:f2510e7fdcd6cfda4ec50014726d4857abf79acfc010084ce8c26091913f1b25" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==3.0.0" + }, "bcrypt": { "hashes": [ "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535", @@ -661,6 +420,14 @@ "markers": "python_version >= '3.7'", "version": "==41.0.4" }, + "dill": { + "hashes": [ + "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", + "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" + ], + "markers": "python_version < '3.11'", + "version": "==0.3.7" + }, "exceptiongroup": { "hashes": [ "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", @@ -686,6 +453,96 @@ "markers": "python_version >= '3.7'", "version": "==2.0.0" }, + "isort": { + "hashes": [ + "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", + "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==5.12.0" + }, + "jinja2": { + "hashes": [ + "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", + "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.2" + }, + "markupsafe": { + "hashes": [ + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", + "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", + "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.3" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, "mypy-extensions": { "hashes": [ "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", @@ -694,6 +551,44 @@ "markers": "python_version >= '3.5'", "version": "==1.0.0" }, + "numpy": { + "hashes": [ + "sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2", + "sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292", + "sha256:0792824ce2f7ea0c82ed2e4fecc29bb86bee0567a080dacaf2e0a01fe7654369", + "sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91", + "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388", + "sha256:186ba67fad3c60dbe8a3abff3b67a91351100f2661c8e2a80364ae6279720299", + "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd", + "sha256:436c8e9a4bdeeee84e3e59614d38c3dbd3235838a877af8c211cfcac8a80b8d3", + "sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2", + "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69", + "sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68", + "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148", + "sha256:5671338034b820c8d58c81ad1dafc0ed5a00771a82fccc71d6438df00302094b", + "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a", + "sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be", + "sha256:7d484292eaeb3e84a51432a94f53578689ffdea3f90e10c8b203a99be5af57d8", + "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505", + "sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c", + "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208", + "sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8", + "sha256:b44e6a09afc12952a7d2a58ca0a2429ee0d49a4f89d83a0a11052da696440e49", + "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95", + "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229", + "sha256:c2e698cb0c6dda9372ea98a0344245ee65bdc1c9dd939cceed6bb91256837896", + "sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f", + "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c", + "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb", + "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99", + "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112", + "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581", + "sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd", + "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf" + ], + "markers": "python_version < '3.10'", + "version": "==1.26.0" + }, "packaging": { "hashes": [ "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", @@ -702,6 +597,39 @@ "markers": "python_version >= '3.7'", "version": "==23.2" }, + "pandas": { + "hashes": [ + "sha256:0183cb04a057cc38fde5244909fca9826d5d57c4a5b7390c0cc3fa7acd9fa883", + "sha256:1fc87eac0541a7d24648a001d553406f4256e744d92df1df8ebe41829a915028", + "sha256:220b98d15cee0b2cd839a6358bd1f273d0356bf964c1a1aeb32d47db0215488b", + "sha256:2552bffc808641c6eb471e55aa6899fa002ac94e4eebfa9ec058649122db5824", + "sha256:315e19a3e5c2ab47a67467fc0362cb36c7c60a93b6457f675d7d9615edad2ebe", + "sha256:344021ed3e639e017b452aa8f5f6bf38a8806f5852e217a7594417fb9bbfa00e", + "sha256:375262829c8c700c3e7cbb336810b94367b9c4889818bbd910d0ecb4e45dc261", + "sha256:457d8c3d42314ff47cc2d6c54f8fc0d23954b47977b2caed09cd9635cb75388b", + "sha256:4aed257c7484d01c9a194d9a94758b37d3d751849c05a0050c087a358c41ad1f", + "sha256:530948945e7b6c95e6fa7aa4be2be25764af53fba93fe76d912e35d1c9ee46f5", + "sha256:5ae7e989f12628f41e804847a8cc2943d362440132919a69429d4dea1f164da0", + "sha256:71f510b0efe1629bf2f7c0eadb1ff0b9cf611e87b73cd017e6b7d6adb40e2b3a", + "sha256:73f219fdc1777cf3c45fde7f0708732ec6950dfc598afc50588d0d285fddaefc", + "sha256:8092a368d3eb7116e270525329a3e5c15ae796ccdf7ccb17839a73b4f5084a39", + "sha256:82ae615826da838a8e5d4d630eb70c993ab8636f0eff13cb28aafc4291b632b5", + "sha256:9608000a5a45f663be6af5c70c3cbe634fa19243e720eb380c0d378666bc7702", + "sha256:a40dd1e9f22e01e66ed534d6a965eb99546b41d4d52dbdb66565608fde48203f", + "sha256:b4f5a82afa4f1ff482ab8ded2ae8a453a2cdfde2001567b3ca24a4c5c5ca0db3", + "sha256:c009a92e81ce836212ce7aa98b219db7961a8b95999b97af566b8dc8c33e9519", + "sha256:c218796d59d5abd8780170c937b812c9637e84c32f8271bbf9845970f8c1351f", + "sha256:cc3cd122bea268998b79adebbb8343b735a5511ec14efb70a39e7acbc11ccbdc", + "sha256:d0d8fd58df5d17ddb8c72a5075d87cd80d71b542571b5f78178fb067fa4e9c72", + "sha256:e18bc3764cbb5e118be139b3b611bc3fbc5d3be42a7e827d1096f46087b395eb", + "sha256:e2b83abd292194f350bb04e188f9379d36b8dfac24dd445d5c87575f3beaf789", + "sha256:e7469271497960b6a781eaa930cba8af400dd59b62ec9ca2f4d31a19f2f91090", + "sha256:e9dbacd22555c2d47f262ef96bb4e30880e5956169741400af8b306bbb24a273", + "sha256:f6257b314fc14958f8122779e5a1557517b0f8e500cfb2bd53fa1f75a8ad0af2" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.2" + }, "paramiko": { "hashes": [ "sha256:6a3777a961ac86dbef375c5f5b8d50014a1a96d0fd7f054a43bc880134b0ff77", @@ -742,6 +670,24 @@ ], "version": "==2.21" }, + "pylint": { + "hashes": [ + "sha256:21da8ed1294f88d66c82eb3e624a0993291613548bb17fbccaa220c31c41293b", + "sha256:d22816c963816d7810b87afe0bdf5c80009e1078ecbb9c8f2e2a24d4430039b1" + ], + "index": "pypi", + "markers": "python_full_version >= '3.8.0'", + "version": "==3.0.0" + }, + "pylint-report": { + "hashes": [ + "sha256:cd13c8500b1eac95fe6523da2c53f8a10fd63514f46bcf36a6b55b15c0efaecd", + "sha256:ff81d52ea818258d5539805dff1d409f82464b958c4dd30d2fa2c4bf5a6c3d62" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.4.0" + }, "pynacl": { "hashes": [ "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", @@ -783,6 +729,13 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.8.2" }, + "pytz": { + "hashes": [ + "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", + "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" + ], + "version": "==2023.3.post1" + }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", @@ -799,12 +752,20 @@ "markers": "python_version < '3.11'", "version": "==2.0.1" }, + "tomlkit": { + "hashes": [ + "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", + "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899" + ], + "markers": "python_version >= '3.7'", + "version": "==0.12.1" + }, "typing-extensions": { "hashes": [ "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" ], - "markers": "python_version < '3.10'", + "markers": "python_version < '3.11'", "version": "==4.8.0" } } diff --git a/requirements-test.txt b/requirements-test.txt index f0e7818..4155983 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -4,4 +4,6 @@ coverage black pytest-cov faker -cprint \ No newline at end of file +cprint +pylint +pylint-report \ No newline at end of file From a5e34253e81c83123deefbe7f1591021c2fdbed4 Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:09:22 +0000 Subject: [PATCH 14/26] Fixed pylint_report command in CI --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 499d5cd..d5f7e39 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -27,7 +27,7 @@ jobs: - name: Run tests run: | - python3 -m pylint src/sshkey_tools | tee | pylint_report --output=report.html + python3 -m pylint src/sshkey_tools | tee | pylint_report -o report.html - name: Upload report uses: actions/upload-artifact@v1 From 644cff91fe5058cca0fb017b956a4ca082715277 Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:06:12 +0000 Subject: [PATCH 15/26] Added logo, badges to readme --- .github/workflows/deploy_testing.yml | 1 + .github/workflows/dev_testing.yml | 1 + .github/workflows/linting.yml | 1 + .github/workflows/publish.yml | 1 + README.md | 9 +++ assets/sshkey-tools-plain.svg | 93 ++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+) create mode 100644 assets/sshkey-tools-plain.svg diff --git a/.github/workflows/deploy_testing.yml b/.github/workflows/deploy_testing.yml index 3590d24..79ce510 100644 --- a/.github/workflows/deploy_testing.yml +++ b/.github/workflows/deploy_testing.yml @@ -1,3 +1,4 @@ +name: "Release Tests" on: release: types: [created] diff --git a/.github/workflows/dev_testing.yml b/.github/workflows/dev_testing.yml index 7c84d47..ead4678 100644 --- a/.github/workflows/dev_testing.yml +++ b/.github/workflows/dev_testing.yml @@ -1,3 +1,4 @@ +name: "Development Tests" on: push: paths-ignore: diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index d5f7e39..8f1f6cb 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -1,3 +1,4 @@ +name: "Pylint" on: push: paths-ignore: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2677b16..29b311d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,3 +1,4 @@ +name: "Build Release" on: release: types: [published] diff --git a/README.md b/README.md index 1ba20ff..7105f47 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,13 @@ +![SSHKey Tools](./assets/sshkey-tools-plain.svg) # sshkey-tools +[![PyPI version](https://badge.fury.io/py/sshkey-tools.svg)](https://badge.fury.io/py/sshkey-tools) +![Linting](https://github.com/scheiblingco/sshkey-tools/actions/workflows/linting.yml/badge.svg) +![Testing-Dev](https://github.com/scheiblingco/sshkey-tools/actions/workflows/dev_testing.yml/badge.svg) +![Testing-Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/deploy_testing.yml/badge.svg) +![Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/linting.yml/publish.svg) +[![CodeQL](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql) + + Python package for managing OpenSSH keypairs and certificates ([protocol.CERTKEYS](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)). Supported functionality includes: diff --git a/assets/sshkey-tools-plain.svg b/assets/sshkey-tools-plain.svg new file mode 100644 index 0000000..0e73a40 --- /dev/null +++ b/assets/sshkey-tools-plain.svg @@ -0,0 +1,93 @@ + + + + + + + + + >_ + + + SSHKEY TOOLS + + From 6febe54e548cd544b20b9fb286ca65d5f35af32e Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:07:02 +0000 Subject: [PATCH 16/26] Fixed badge for build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7105f47..409db8c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Linting](https://github.com/scheiblingco/sshkey-tools/actions/workflows/linting.yml/badge.svg) ![Testing-Dev](https://github.com/scheiblingco/sshkey-tools/actions/workflows/dev_testing.yml/badge.svg) ![Testing-Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/deploy_testing.yml/badge.svg) -![Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/linting.yml/publish.svg) +![Build](https://github.com/scheiblingco/sshkey-tools/actions/workflows/publish.yml/badge.svg) [![CodeQL](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/scheiblingco/sshkey-tools/actions/workflows/github-code-scanning/codeql) From 1c6e86c461bc8d89f1ded7ff57d07ef1412cbe84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:26:10 +0000 Subject: [PATCH 17/26] Bump black from 23.9.1 to 23.10.0 Bumps [black](https://github.com/psf/black) from 23.9.1 to 23.10.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.9.1...23.10.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 116 +++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index f5e735f..ee0cd59 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -194,11 +194,11 @@ }, "astroid": { "hashes": [ - "sha256:1defdbca052635dd29657ea674edfc45e4b5be9cd53630c5b084fcfed94344a8", - "sha256:f2510e7fdcd6cfda4ec50014726d4857abf79acfc010084ce8c26091913f1b25" + "sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca", + "sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e" ], "markers": "python_full_version >= '3.8.0'", - "version": "==3.0.0" + "version": "==3.0.1" }, "bcrypt": { "hashes": [ @@ -230,32 +230,28 @@ }, "black": { "hashes": [ - "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f", - "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7", - "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100", - "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573", - "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d", - "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f", - "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9", - "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300", - "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948", - "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325", - "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9", - "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71", - "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186", - "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f", - "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe", - "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855", - "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80", - "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393", - "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c", - "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204", - "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377", - "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301" + "sha256:0e232f24a337fed7a82c1185ae46c56c4a6167fb0fe37411b43e876892c76699", + "sha256:30b78ac9b54cf87bcb9910ee3d499d2bc893afd52495066c49d9ee6b21eee06e", + "sha256:31946ec6f9c54ed7ba431c38bc81d758970dd734b96b8e8c2b17a367d7908171", + "sha256:31b9f87b277a68d0e99d2905edae08807c007973eaa609da5f0c62def6b7c0bd", + "sha256:47c4510f70ec2e8f9135ba490811c071419c115e46f143e4dce2ac45afdcf4c9", + "sha256:481167c60cd3e6b1cb8ef2aac0f76165843a374346aeeaa9d86765fe0dd0318b", + "sha256:6901631b937acbee93c75537e74f69463adaf34379a04eef32425b88aca88a23", + "sha256:76baba9281e5e5b230c9b7f83a96daf67a95e919c2dfc240d9e6295eab7b9204", + "sha256:7fb5fc36bb65160df21498d5a3dd330af8b6401be3f25af60c6ebfe23753f747", + "sha256:960c21555be135c4b37b7018d63d6248bdae8514e5c55b71e994ad37407f45b8", + "sha256:a3c2ddb35f71976a4cfeca558848c2f2f89abc86b06e8dd89b5a65c1e6c0f22a", + "sha256:c870bee76ad5f7a5ea7bd01dc646028d05568d33b0b09b7ecfc8ec0da3f3f39c", + "sha256:d3d9129ce05b0829730323bdcb00f928a448a124af5acf90aa94d9aba6969604", + "sha256:db451a3363b1e765c172c3fd86213a4ce63fb8524c938ebd82919bf2a6e28c6a", + "sha256:e223b731a0e025f8ef427dd79d8cd69c167da807f5710add30cdf131f13dd62e", + "sha256:f20ff03f3fdd2fd4460b4f631663813e57dc277e37fb216463f3b907aa5a9bdd", + "sha256:f74892b4b836e5162aa0452393112a574dac85e13902c57dfbaaf388e4eda37c", + "sha256:f8dc7d50d94063cdfd13c82368afd8588bac4ce360e4224ac399e769d6704e98" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==23.9.1" + "version": "==23.10.0" }, "cffi": { "hashes": [ @@ -553,41 +549,41 @@ }, "numpy": { "hashes": [ - "sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2", - "sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292", - "sha256:0792824ce2f7ea0c82ed2e4fecc29bb86bee0567a080dacaf2e0a01fe7654369", - "sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91", - "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388", - "sha256:186ba67fad3c60dbe8a3abff3b67a91351100f2661c8e2a80364ae6279720299", - "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd", - "sha256:436c8e9a4bdeeee84e3e59614d38c3dbd3235838a877af8c211cfcac8a80b8d3", - "sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2", - "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69", - "sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68", - "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148", - "sha256:5671338034b820c8d58c81ad1dafc0ed5a00771a82fccc71d6438df00302094b", - "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a", - "sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be", - "sha256:7d484292eaeb3e84a51432a94f53578689ffdea3f90e10c8b203a99be5af57d8", - "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505", - "sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c", - "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208", - "sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8", - "sha256:b44e6a09afc12952a7d2a58ca0a2429ee0d49a4f89d83a0a11052da696440e49", - "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95", - "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229", - "sha256:c2e698cb0c6dda9372ea98a0344245ee65bdc1c9dd939cceed6bb91256837896", - "sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f", - "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c", - "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb", - "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99", - "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112", - "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581", - "sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd", - "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf" + "sha256:06934e1a22c54636a059215d6da99e23286424f316fddd979f5071093b648668", + "sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9", + "sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f", + "sha256:1e11668d6f756ca5ef534b5be8653d16c5352cbb210a5c2a79ff288e937010d5", + "sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53", + "sha256:59227c981d43425ca5e5c01094d59eb14e8772ce6975d4b2fc1e106a833d5ae2", + "sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974", + "sha256:6965888d65d2848e8768824ca8288db0a81263c1efccec881cb35a0d805fcd2f", + "sha256:76ff661a867d9272cd2a99eed002470f46dbe0943a5ffd140f49be84f68ffc42", + "sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2", + "sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af", + "sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67", + "sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e", + "sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c", + "sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7", + "sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e", + "sha256:a5b411040beead47a228bde3b2241100454a6abde9df139ed087bd73fc0a4908", + "sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66", + "sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24", + "sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b", + "sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e", + "sha256:c8c6c72d4a9f831f328efb1312642a1cafafaa88981d9ab76368d50d07d93cbe", + "sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a", + "sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575", + "sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297", + "sha256:d1d2c6b7dd618c41e202c59c1413ef9b2c8e8a15f5039e344af64195459e3104", + "sha256:d2984cb6caaf05294b8466966627e80bf6c7afd273279077679cb010acb0e5ab", + "sha256:d58e8c51a7cf43090d124d5073bc29ab2755822181fcad978b12e144e5e5a4b3", + "sha256:d78f269e0c4fd365fc2992c00353e4530d274ba68f15e968d8bc3c69ce5f5244", + "sha256:dcfaf015b79d1f9f9c9fd0731a907407dc3e45769262d657d754c3a028586124", + "sha256:e44ccb93f30c75dfc0c3aa3ce38f33486a75ec9abadabd4e59f114994a9c4617", + "sha256:e509cbc488c735b43b5ffea175235cec24bbc57b227ef1acc691725beb230d1c" ], "markers": "python_version < '3.10'", - "version": "==1.26.0" + "version": "==1.26.1" }, "packaging": { "hashes": [ @@ -765,7 +761,7 @@ "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" ], - "markers": "python_version < '3.11'", + "markers": "python_version < '3.10'", "version": "==4.8.0" } } From d50eace1e4c7dfe3d27d1c3b153d699206bcd694 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:24:49 +0000 Subject: [PATCH 18/26] Bump faker from 19.6.2 to 19.11.0 Bumps [faker](https://github.com/joke2k/faker) from 19.6.2 to 19.11.0. - [Release notes](https://github.com/joke2k/faker/releases) - [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.md) - [Commits](https://github.com/joke2k/faker/compare/v19.6.2...v19.11.0) --- updated-dependencies: - dependency-name: faker dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index ee0cd59..1a080df 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -434,12 +434,12 @@ }, "faker": { "hashes": [ - "sha256:8fba91068dc26e3159c1ac9f22444a2338704b0991d86605322e454bda420092", - "sha256:d5d5953556b0fb428a46019e03fc2d40eab2980135ddef5a9eb3d054947fdf83" + "sha256:a62a3fd3bfa3122d4f57dfa26a1cc37d76751a76c8ddd63cf9d24078c57913a4", + "sha256:e28090068293c5a83e7f4d636417d45fae1031ca8a8136cc2415549ebc2111e2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==19.6.2" + "version": "==19.11.0" }, "iniconfig": { "hashes": [ From 6a2a975af3819daa52a9c109093231b424d5aeb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:23:36 +0000 Subject: [PATCH 19/26] Bump pylint from 3.0.0 to 3.0.2 Bumps [pylint](https://github.com/pylint-dev/pylint) from 3.0.0 to 3.0.2. - [Release notes](https://github.com/pylint-dev/pylint/releases) - [Commits](https://github.com/pylint-dev/pylint/compare/v3.0.0...v3.0.2) --- updated-dependencies: - dependency-name: pylint dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 1a080df..6afd178 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -668,12 +668,12 @@ }, "pylint": { "hashes": [ - "sha256:21da8ed1294f88d66c82eb3e624a0993291613548bb17fbccaa220c31c41293b", - "sha256:d22816c963816d7810b87afe0bdf5c80009e1078ecbb9c8f2e2a24d4430039b1" + "sha256:0d4c286ef6d2f66c8bfb527a7f8a629009e42c99707dec821a03e1b51a4c1496", + "sha256:60ed5f3a9ff8b61839ff0348b3624ceeb9e6c2a92c514d81c9cc273da3b6bcda" ], "index": "pypi", "markers": "python_full_version >= '3.8.0'", - "version": "==3.0.0" + "version": "==3.0.2" }, "pylint-report": { "hashes": [ From 43a89c00fa0a48bdb2a59ce76ac3b3b9534d1a42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:51:07 +0000 Subject: [PATCH 20/26] Bump black from 23.10.0 to 23.10.1 Bumps [black](https://github.com/psf/black) from 23.10.0 to 23.10.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.10.0...23.10.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6afd178..659e4bf 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -230,28 +230,28 @@ }, "black": { "hashes": [ - "sha256:0e232f24a337fed7a82c1185ae46c56c4a6167fb0fe37411b43e876892c76699", - "sha256:30b78ac9b54cf87bcb9910ee3d499d2bc893afd52495066c49d9ee6b21eee06e", - "sha256:31946ec6f9c54ed7ba431c38bc81d758970dd734b96b8e8c2b17a367d7908171", - "sha256:31b9f87b277a68d0e99d2905edae08807c007973eaa609da5f0c62def6b7c0bd", - "sha256:47c4510f70ec2e8f9135ba490811c071419c115e46f143e4dce2ac45afdcf4c9", - "sha256:481167c60cd3e6b1cb8ef2aac0f76165843a374346aeeaa9d86765fe0dd0318b", - "sha256:6901631b937acbee93c75537e74f69463adaf34379a04eef32425b88aca88a23", - "sha256:76baba9281e5e5b230c9b7f83a96daf67a95e919c2dfc240d9e6295eab7b9204", - "sha256:7fb5fc36bb65160df21498d5a3dd330af8b6401be3f25af60c6ebfe23753f747", - "sha256:960c21555be135c4b37b7018d63d6248bdae8514e5c55b71e994ad37407f45b8", - "sha256:a3c2ddb35f71976a4cfeca558848c2f2f89abc86b06e8dd89b5a65c1e6c0f22a", - "sha256:c870bee76ad5f7a5ea7bd01dc646028d05568d33b0b09b7ecfc8ec0da3f3f39c", - "sha256:d3d9129ce05b0829730323bdcb00f928a448a124af5acf90aa94d9aba6969604", - "sha256:db451a3363b1e765c172c3fd86213a4ce63fb8524c938ebd82919bf2a6e28c6a", - "sha256:e223b731a0e025f8ef427dd79d8cd69c167da807f5710add30cdf131f13dd62e", - "sha256:f20ff03f3fdd2fd4460b4f631663813e57dc277e37fb216463f3b907aa5a9bdd", - "sha256:f74892b4b836e5162aa0452393112a574dac85e13902c57dfbaaf388e4eda37c", - "sha256:f8dc7d50d94063cdfd13c82368afd8588bac4ce360e4224ac399e769d6704e98" + "sha256:037e9b4664cafda5f025a1728c50a9e9aedb99a759c89f760bd83730e76ba884", + "sha256:1b917a2aa020ca600483a7b340c165970b26e9029067f019e3755b56e8dd5916", + "sha256:1f8ce316753428ff68749c65a5f7844631aa18c8679dfd3ca9dc1a289979c258", + "sha256:33d40f5b06be80c1bbce17b173cda17994fbad096ce60eb22054da021bf933d1", + "sha256:3f157a8945a7b2d424da3335f7ace89c14a3b0625e6593d21139c2d8214d55ce", + "sha256:5ed45ac9a613fb52dad3b61c8dea2ec9510bf3108d4db88422bacc7d1ba1243d", + "sha256:6d23d7822140e3fef190734216cefb262521789367fbdc0b3f22af6744058982", + "sha256:7670242e90dc129c539e9ca17665e39a146a761e681805c54fbd86015c7c84f7", + "sha256:7b4d10b0f016616a0d93d24a448100adf1699712fb7a4efd0e2c32bbb219b173", + "sha256:7cb5936e686e782fddb1c73f8aa6f459e1ad38a6a7b0e54b403f1f05a1507ee9", + "sha256:7d56124b7a61d092cb52cce34182a5280e160e6aff3137172a68c2c2c4b76bcb", + "sha256:840015166dbdfbc47992871325799fd2dc0dcf9395e401ada6d88fe11498abad", + "sha256:9c74de4c77b849e6359c6f01987e94873c707098322b91490d24296f66d067dc", + "sha256:b15b75fc53a2fbcac8a87d3e20f69874d161beef13954747e053bca7a1ce53a0", + "sha256:cfcce6f0a384d0da692119f2d72d79ed07c7159879d0bb1bb32d2e443382bf3a", + "sha256:d431e6739f727bb2e0495df64a6c7a5310758e87505f5f8cde9ff6c0f2d7e4fe", + "sha256:e293e4c2f4a992b980032bbd62df07c1bcff82d6964d6c9496f2cd726e246ace", + "sha256:ec3f8e6234c4e46ff9e16d9ae96f4ef69fa328bb4ad08198c8cee45bb1f08c69" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==23.10.0" + "version": "==23.10.1" }, "cffi": { "hashes": [ @@ -702,11 +702,11 @@ }, "pytest": { "hashes": [ - "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002", - "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069" + "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", + "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" ], "markers": "python_version >= '3.7'", - "version": "==7.4.2" + "version": "==7.4.3" }, "pytest-cov": { "hashes": [ @@ -761,7 +761,7 @@ "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" ], - "markers": "python_version < '3.10'", + "markers": "python_version < '3.11'", "version": "==4.8.0" } } From 9176e10c281644a3503cce14122678623b30359f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:49:26 +0000 Subject: [PATCH 21/26] Bump faker from 19.11.0 to 19.13.0 Bumps [faker](https://github.com/joke2k/faker) from 19.11.0 to 19.13.0. - [Release notes](https://github.com/joke2k/faker/releases) - [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.md) - [Commits](https://github.com/joke2k/faker/compare/v19.11.0...v19.13.0) --- updated-dependencies: - dependency-name: faker dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 659e4bf..10bd3b0 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -434,12 +434,12 @@ }, "faker": { "hashes": [ - "sha256:a62a3fd3bfa3122d4f57dfa26a1cc37d76751a76c8ddd63cf9d24078c57913a4", - "sha256:e28090068293c5a83e7f4d636417d45fae1031ca8a8136cc2415549ebc2111e2" + "sha256:14ccb0aec342d33aa3889a864a56e5b3c2d56bce1b89f9189f4fbc128b9afc1e", + "sha256:da880a76322db7a879c848a0771e129338e0a680a9f695fd9a3e7a6ac82b45e1" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==19.11.0" + "version": "==19.13.0" }, "iniconfig": { "hashes": [ From d4a6bbde94f33f666e256de51e9313283d0d8961 Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:51:33 +0000 Subject: [PATCH 22/26] Finished signature decoder --- .devcontainer/devcontainer.json | 3 -- .gitignore | 9 +++- decode_signature.py | 41 +++++++++++++++ make_signature.sh | 4 ++ src/sshkey_tools/fields.py | 88 +++++++++++++++++++++++++++++++++ src/sshkey_tools/signatures.py | 68 +++++++++++++++++++++++++ 6 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 decode_signature.py create mode 100644 make_signature.sh create mode 100644 src/sshkey_tools/signatures.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 42c2e00..b758f36 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,4 @@ { - "build": { - "dockerfile": "dev.Dockerfile" - }, "customizations": { "vscode": { "extensions": [ diff --git a/.gitignore b/.gitignore index ef8570c..31fbd50 100644 --- a/.gitignore +++ b/.gitignore @@ -148,4 +148,11 @@ report.html test_certificate testing.py .idea -core \ No newline at end of file +core +/id_rsa +/id_rsa.pub +/id_ecdsa* +/id_dsa* +/id_ed25519* +hello.txt +hello.txt.sig \ No newline at end of file diff --git a/decode_signature.py b/decode_signature.py new file mode 100644 index 0000000..89e87d7 --- /dev/null +++ b/decode_signature.py @@ -0,0 +1,41 @@ +from src.sshkey_tools import ( + fields as _F, + keys as _K, + exceptions as _E, + signatures as _S +) +from base64 import b64decode +with open("hello.txt.sig", 'rb') as f: + fx = f.read() + +data = b''.join(fx.split(b'\n')[1:-2]) +data = b64decode(data) + +# # Get preamble +# preamble, data = data[0:6], data[6:] + +# # Get version +# version, data = _F.Integer32Field.decode(data) + +# # Get public key +# pubkey, data = _F.BytestringField.decode(data) +# pubkey_type, pubkey = _F.BytestringField.decode(pubkey) + +# # Get namespace +# namespace, data = _F.StringField.decode(data) + +# # Get reserved +# reserved, data = _F.ReservedField.decode(data) + +# # Get hash alg +# hash_alg, data = _F.BytestringField.decode(data) + +# # Get signature +# signature, data = _F.SignatureField.from_decode(data) +# # sigtype, signature = _F.BytestringField.decode(signature) + +sign = _S.SSHSignature.decode(data) + + + +print() \ No newline at end of file diff --git a/make_signature.sh b/make_signature.sh new file mode 100644 index 0000000..fe1f9f9 --- /dev/null +++ b/make_signature.sh @@ -0,0 +1,4 @@ +#!/bin/bash +ssh-keygen -t rsa -b 4096 -f id_rsa -N '' +echo "Hello World" > hello.txt +ssh-keygen -Y sign -n hello@world -f id_rsa hello.txt \ No newline at end of file diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index 638c6d5..bde2c34 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -67,6 +67,16 @@ b"ed25519": "Ed25519SignatureField", } +PUBKEY_FIELD_TYPE_MAP = { + b"ssh-rsa": "RsaPubkeyField", + b"rsa-sha2-256": "RsaPubkeyField", + b"rsa-sha2-512": "RsaPubkeyField", + b"ecdsa-sha2-nistp256": "EcdsaPubkeyField", + b"ecdsa-sha2-nistp384": "EcdsaPubkeyField", + b"ecdsa-sha2-nistp521": "EcdsaPubkeyField", + b"ssh-ed25519": "Ed25519PubkeyField", +} + class CERT_TYPE(Enum): """ @@ -814,6 +824,27 @@ def __str__(self) -> str: ] ) + @classmethod + def decode(cls, data: bytes) -> "PublicKeyField": + """ + Decode from a packed pair of key type and key data + + Args: + data (bytes): Packed key type and data + + Raises: + _EX.InvalidKeyException: Invalid + + Returns: + PublicKeyField: A public key field + """ + pk_data, data = BytestringField.decode(data) + pk_type, pk_data = BytestringField.decode(pk_data) + + target_class = globals()[PUBKEY_FIELD_TYPE_MAP[pk_type]] + + return target_class.from_decode(pk_data)[0], data + @classmethod def encode(cls, value: PublicKey) -> bytes: """ @@ -1685,3 +1716,60 @@ def sign(self, data: bytes, **kwargs) -> None: """ self.value = self.private_key.sign(data) self.is_signed = True + + +class SshsigField(CertificateField): + """ + SSH Signature magic preamble field (static b'SSHSIG') + """ + DATA_TYPE = bytes + DEFAULT = b'SSHSIG' + @classmethod + def encode(cls, value: bytes) -> bytes: + """ + Encodes the SSH Signature magic preamble field + + Args: + value (bytes): The SSH Signature magic preamble field + + Returns: + bytes: The SSH Signature magic preamble field + """ + return b'SSHSIG' + + @classmethod + def decode(cls, data: bytes) -> Tuple[bytes, bytes]: + """ + Decodes the SSH Signature magic preamble field + + Args: + data (bytes): The SSH Signature magic preamble field + + Returns: + bytes: The SSH Signature magic preamble field + """ + return data[:6], data[6:] + + def __validate_value__(self) -> Union[bool, Exception]: + """ + Validates the contents of the field + """ + return True + +class SignatureVersionField(Integer32Field): + DATA_TYPE = int + DEFAULT = 1 + +class SignatureNamespace(StringField): + DATA_TYPE = (str, bytes) + DEFAULT = "" + + def __validate_value__(self) -> Union[bool, Exception]: + if len(self.value) == 0: + return _EX.InvalidFieldDataException( + f"{self.get_name()} must be a non-empty string" + ) + +class SignatureHashAlgorithmField(StringField): + DATA_TYPE = (str, bytes) + DEFAULT = "" diff --git a/src/sshkey_tools/signatures.py b/src/sshkey_tools/signatures.py new file mode 100644 index 0000000..c310f82 --- /dev/null +++ b/src/sshkey_tools/signatures.py @@ -0,0 +1,68 @@ +# Format: + +# byte[6] MAGIC_PREAMBLE (SSHSIG) +# uint32 SIG_VERSION (0x01) +# string publickey +# string namespace +# string reserved +# string hash_algorithm +# string signature + +from .cert import Fieldset, dataclass, Union +from . import ( + fields as _FIELD, + keys as _KEY, + exceptions as _EX +) + +@dataclass +class SignatureFieldset(Fieldset): + """Fields for SSH Signature""" + + DECODE_ORDER = [ + "magic_preamble", + "sig_version", + "public_key", + "namespace", + "reserved", + "hash_algorithm", + "signature" + ] + + magic_preamble: _FIELD.SshsigField = _FIELD.SshsigField.factory + sig_version: _FIELD.SignatureVersionField = _FIELD.SignatureVersionField.factory + public_key: _FIELD.PublicKeyField = _FIELD.PublicKeyField.factory + + namespace: _FIELD.StringField = _FIELD.StringField.factory + reserved: _FIELD.ReservedField = _FIELD.ReservedField.factory + hash_algorithm: _FIELD.SignatureHashAlgorithmField = _FIELD.SignatureHashAlgorithmField.factory + signature: _FIELD.SignatureField = _FIELD.SignatureField.factory + +class SSHSignature: + """ + General class for SSH Signatures, used for loading and parsing. + """ + def __init__( + self, signer_privkey: _KEY.PrivateKey = None, + fields: SignatureFieldset = SignatureFieldset + ): + self.fields = fields() if isinstance(fields, type) else fields + + if isinstance(signer_privkey, type) and signer_privkey is not None: + self.fields.replace_field( + "signature", _FIELD.SignatureField.from_object(signer_privkey) + ) + + @classmethod + def decode(cls, data: bytes) -> "SSHSignature": + """ + Loads an existing SSH Signature from byte contents + + Args: + data (bytes): The normalized byte data from the .sig-file + + Returns: + SSHSignature: The parsed SSH Signature + """ + sig_fields, data = SignatureFieldset.decode(data) + return cls(fields=sig_fields) \ No newline at end of file From fc2d5dc912a4a5364568ef84aecf2443cdafea8e Mon Sep 17 00:00:00 2001 From: Lars Scheibling <24367830+scheibling@users.noreply.github.com> Date: Fri, 10 Nov 2023 21:27:34 +0000 Subject: [PATCH 23/26] Signature validation for file now working --- .gitignore | 4 +- decode_signature.py | 41 ------------ make_signature.sh | 9 ++- src/sshkey_tools/fields.py | 31 ++++++++- src/sshkey_tools/signatures.py | 116 +++++++++++++++++++++++++++++++-- src/sshkey_tools/utils.py | 23 +++++++ validate_signatures.py | 41 ++++++++++++ 7 files changed, 214 insertions(+), 51 deletions(-) delete mode 100644 decode_signature.py mode change 100644 => 100755 make_signature.sh create mode 100644 validate_signatures.py diff --git a/.gitignore b/.gitignore index 31fbd50..17a3ec2 100644 --- a/.gitignore +++ b/.gitignore @@ -155,4 +155,6 @@ core /id_dsa* /id_ed25519* hello.txt -hello.txt.sig \ No newline at end of file +hello.txt.sig + +/testkeys \ No newline at end of file diff --git a/decode_signature.py b/decode_signature.py deleted file mode 100644 index 89e87d7..0000000 --- a/decode_signature.py +++ /dev/null @@ -1,41 +0,0 @@ -from src.sshkey_tools import ( - fields as _F, - keys as _K, - exceptions as _E, - signatures as _S -) -from base64 import b64decode -with open("hello.txt.sig", 'rb') as f: - fx = f.read() - -data = b''.join(fx.split(b'\n')[1:-2]) -data = b64decode(data) - -# # Get preamble -# preamble, data = data[0:6], data[6:] - -# # Get version -# version, data = _F.Integer32Field.decode(data) - -# # Get public key -# pubkey, data = _F.BytestringField.decode(data) -# pubkey_type, pubkey = _F.BytestringField.decode(pubkey) - -# # Get namespace -# namespace, data = _F.StringField.decode(data) - -# # Get reserved -# reserved, data = _F.ReservedField.decode(data) - -# # Get hash alg -# hash_alg, data = _F.BytestringField.decode(data) - -# # Get signature -# signature, data = _F.SignatureField.from_decode(data) -# # sigtype, signature = _F.BytestringField.decode(signature) - -sign = _S.SSHSignature.decode(data) - - - -print() \ No newline at end of file diff --git a/make_signature.sh b/make_signature.sh old mode 100644 new mode 100755 index fe1f9f9..5a18fc9 --- a/make_signature.sh +++ b/make_signature.sh @@ -1,4 +1,9 @@ #!/bin/bash ssh-keygen -t rsa -b 4096 -f id_rsa -N '' -echo "Hello World" > hello.txt -ssh-keygen -Y sign -n hello@world -f id_rsa hello.txt \ No newline at end of file +ssh-keygen -t ecdsa -f id_ecdsa -N '' +ssh-keygen -t ed25519 -f id_ed25519 -N '' +echo "Hello World" | tee rsa.txt | tee ecdsa.txt | tee ed25519.txt + +ssh-keygen -Y sign -n hello@world -f id_rsa rsa.txt +ssh-keygen -Y sign -n hello@world -f id_ecdsa ecdsa.txt +ssh-keygen -Y sign -n hello@world -f id_ed25519 ed25519.txt \ No newline at end of file diff --git a/src/sshkey_tools/fields.py b/src/sshkey_tools/fields.py index bde2c34..ed80b3a 100644 --- a/src/sshkey_tools/fields.py +++ b/src/sshkey_tools/fields.py @@ -1712,7 +1712,7 @@ def sign(self, data: bytes, **kwargs) -> None: Args: data (bytes): The data to be signed hash_alg (RsaAlgs, optional): The RSA algorithm to use for hashing. - Defaults to RsaAlgs.SHA256. + Defaults to RsaAlgs.SHA256. """ self.value = self.private_key.sign(data) self.is_signed = True @@ -1759,8 +1759,23 @@ def __validate_value__(self) -> Union[bool, Exception]: class SignatureVersionField(Integer32Field): DATA_TYPE = int DEFAULT = 1 + + def __validate_value__(self) -> Union[bool, Exception]: + """ + Validates the contents of the field + """ + if self.value != 1: + return _EX.InvalidCertificateFieldException( + "The certificate version is invalid" + ) + + return True + +class SignatureNamespaceField(StringField): + DATA_TYPE = (str, bytes) + DEFAULT = "" -class SignatureNamespace(StringField): +class SignatureNamespaceField(StringField): DATA_TYPE = (str, bytes) DEFAULT = "" @@ -1769,7 +1784,17 @@ def __validate_value__(self) -> Union[bool, Exception]: return _EX.InvalidFieldDataException( f"{self.get_name()} must be a non-empty string" ) + + return True class SignatureHashAlgorithmField(StringField): DATA_TYPE = (str, bytes) - DEFAULT = "" + DEFAULT = "sha512" + + def __validate_value__(self) -> Union[bool, Exception]: + if self.value not in ("sha256", "sha512"): + return _EX.InvalidFieldDataException( + f"{self.get_name()} must be one of 'sha256' or 'sha512'" + ) + + return True diff --git a/src/sshkey_tools/signatures.py b/src/sshkey_tools/signatures.py index c310f82..c5ea51c 100644 --- a/src/sshkey_tools/signatures.py +++ b/src/sshkey_tools/signatures.py @@ -7,12 +7,15 @@ # string reserved # string hash_algorithm # string signature - +from base64 import b64decode from .cert import Fieldset, dataclass, Union +from prettytable import PrettyTable +from .utils import concat_to_bytestring, concat_to_string, ensure_bytestring from . import ( fields as _FIELD, keys as _KEY, - exceptions as _EX + exceptions as _EX, + utils as _U ) @dataclass @@ -33,11 +36,19 @@ class SignatureFieldset(Fieldset): sig_version: _FIELD.SignatureVersionField = _FIELD.SignatureVersionField.factory public_key: _FIELD.PublicKeyField = _FIELD.PublicKeyField.factory - namespace: _FIELD.StringField = _FIELD.StringField.factory + namespace: _FIELD.SignatureNamespaceField = _FIELD.StringField.factory reserved: _FIELD.ReservedField = _FIELD.ReservedField.factory hash_algorithm: _FIELD.SignatureHashAlgorithmField = _FIELD.SignatureHashAlgorithmField.factory signature: _FIELD.SignatureField = _FIELD.SignatureField.factory + def __bytes__(self): + return concat_to_bytestring( + bytes(self.magic_preamble), + bytes(self.namespace), + bytes(self.reserved), + bytes(self.hash_algorithm) + ) + class SSHSignature: """ General class for SSH Signatures, used for loading and parsing. @@ -53,6 +64,47 @@ def __init__( "signature", _FIELD.SignatureField.from_object(signer_privkey) ) + @classmethod + def from_file(cls, path: str, encoding: str = 'none') -> "SSHSignature": + """ + Loads an existing SSH Signature from a file + + Args: + path (str): The path to the file + encoding (str, optional): The encoding of the file. None will load the byte content directly. Defaults to 'utf-8'. + + Returns: + SSHSignature: SSH Signature Object + """ + with open(path, 'rb' if encoding == 'none' else 'r') as f: + data = f.read() + + return cls.from_string(data, encoding if encoding != 'none' else None) + + @classmethod + def from_string(cls, data: Union[str, bytes], encoding: str = 'utf-8') -> "SSHSignature": + """ + Loads an existing SSH Signature from file contents/string + + Args: + data (str): The normalized string data from the .sig-file + + Returns: + SSHSignature: The parsed SSH Signature + """ + if isinstance(data, str): + data = data.encode(encoding) + + if b'BEGIN SSH SIGNATURE' in data: + data = data.replace(b'-----BEGIN SSH SIGNATURE-----\n', b'') + + if b'END SSH SIGNATURE' in data: + data = data.replace(b'-----END SSH SIGNATURE-----', b'') + + data = data.strip(b"\n \t") + + return cls.decode(b64decode(data)) + @classmethod def decode(cls, data: bytes) -> "SSHSignature": """ @@ -65,4 +117,60 @@ def decode(cls, data: bytes) -> "SSHSignature": SSHSignature: The parsed SSH Signature """ sig_fields, data = SignatureFieldset.decode(data) - return cls(fields=sig_fields) \ No newline at end of file + return cls(fields=sig_fields) + + def get_signable(self, data: Union[str, bytes]) -> bytes: + """ + Returns the signable data for the signature or verification + + Returns: + bytes: The signable data + """ + hash = b"" + if self.fields.hash_algorithm.value == "sha256": + hash = _U.sha256_hash(ensure_bytestring(data)) + elif self.fields.hash_algorithm.value == "sha512": + hash = _U.sha512_hash(ensure_bytestring(data)) + else: + raise _EX.InvalidHashAlgorithmException( + f"Unknown hash algorithm {self.fields.hash_algorithm}" + ) + + return bytes(self.fields) + _FIELD.StringField.encode(hash) + + def __str__(self) -> str: + table = PrettyTable(["Field", "Value"]) + + for item in (self.header, self.fields, self.footer): + for row in item.__table__(): + table.add_row(row) + + return str(table) + + def get(self, field: str): + if field in self.fields.getattrs(): + return self.fields.get(field, None) + + raise _EX.InvalidCertificateFieldException(f"Unknown field {field}") + + def set(self, field: str, data): + if field in self.fields.getattrs(): + self.fields.set(field, data) + + raise _EX.InvalidCertificateFieldException(f"Unknown field {field}") + + def verify( + self, data, public_key: _KEY.PublicKey = None, raise_on_error: bool = False + ) -> bool: + if not public_key: + public_key = self.get('public_key').value + + public_key.verify( + self.get_signable(data), + self.fields.signature.value + ) + + + + print() + \ No newline at end of file diff --git a/src/sshkey_tools/utils.py b/src/sshkey_tools/utils.py index 1eef0a1..0e4be3a 100644 --- a/src/sshkey_tools/utils.py +++ b/src/sshkey_tools/utils.py @@ -198,6 +198,29 @@ def md5_fingerprint(data: bytes, prefix: bool = True) -> str: a + b for a, b in zip(digest[::2], digest[1::2]) ) +def sha256_hash(data: bytes) -> str: + """ + Returns a SHA256 hash of the given data. + + Args: + data (bytes): The data to hash + + Returns: + str: The hash + """ + return hl.sha256(data).digest() + +def sha512_hash(data: bytes) -> str: + """ + Returns a SHA512 hash of the given data. + + Args: + data (bytes): The data to hash + + Returns: + str: The hash + """ + return hl.sha512(data).digest() def sha256_fingerprint(data: bytes, prefix: bool = True) -> str: """ diff --git a/validate_signatures.py b/validate_signatures.py new file mode 100644 index 0000000..24330ef --- /dev/null +++ b/validate_signatures.py @@ -0,0 +1,41 @@ +from src.sshkey_tools import ( + fields as _F, + keys as _K, + exceptions as _E, + signatures as _S +) + + +# Validate files created with ssh-keygen (WORKS!) +rsa_pub = _K.PublicKey.from_file('testkeys/id_rsa.pub') +ecdsa_pub = _K.PublicKey.from_file('testkeys/id_ecdsa.pub') +ed25519_pub = _K.PublicKey.from_file('testkeys/id_ed25519.pub') + +rsa_sign = _S.SSHSignature.from_file('testkeys/rsa.txt.sig') +ecdsa_sign = _S.SSHSignature.from_file('testkeys/ecdsa.txt.sig') +ed25519_sign = _S.SSHSignature.from_file('testkeys/ed25519.txt.sig') + +rsa_data = open('rsa.txt', 'rb').read() +ecdsa_data = open('ecdsa.txt', 'rb').read() +ed25519_data = open('ed25519.txt', 'rb').read() + +rsa_signable = rsa_sign.get_signable(rsa_data) +ecdsa_signable = ecdsa_sign.get_signable(ecdsa_data) +ed25519_signable = ed25519_sign.get_signable(ed25519_data) + +try: + rsa_pub.verify(rsa_signable, rsa_sign.fields.signature.value) +except: + print("RSA validation failed") + +try: + ecdsa_pub.verify(ecdsa_signable, ecdsa_sign.fields.signature.value) +except: + print("ECDSA validation failed") + +try: + ed25519_pub.verify(ed25519_signable, ed25519_sign.fields.signature.value) +except: + print("Ed25519 validation failed") + +print() From 6101b0f14db14176a4e743ffd962adc0b44b5823 Mon Sep 17 00:00:00 2001 From: Lars Date: Sun, 31 Dec 2023 15:02:03 +0100 Subject: [PATCH 24/26] Signing and validation now works Todo: export to file --- make_signature.sh | 14 +++++----- src/sshkey_tools/cert.py | 3 ++- src/sshkey_tools/signatures.py | 38 +++++++++++++++++++++----- validate_signatures.py | 49 +++++++++++++++++++++++++--------- 4 files changed, 77 insertions(+), 27 deletions(-) diff --git a/make_signature.sh b/make_signature.sh index 5a18fc9..28dc276 100755 --- a/make_signature.sh +++ b/make_signature.sh @@ -1,9 +1,9 @@ #!/bin/bash -ssh-keygen -t rsa -b 4096 -f id_rsa -N '' -ssh-keygen -t ecdsa -f id_ecdsa -N '' -ssh-keygen -t ed25519 -f id_ed25519 -N '' -echo "Hello World" | tee rsa.txt | tee ecdsa.txt | tee ed25519.txt +ssh-keygen -t rsa -b 4096 -f testkeys/id_rsa -N '' +ssh-keygen -t ecdsa -f testkeys/id_ecdsa -N '' +ssh-keygen -t ed25519 -f testkeys/id_ed25519 -N '' +echo "Hello World" | tee testkeys/rsa.txt | tee testkeys/ecdsa.txt | tee testkeys/ed25519.txt -ssh-keygen -Y sign -n hello@world -f id_rsa rsa.txt -ssh-keygen -Y sign -n hello@world -f id_ecdsa ecdsa.txt -ssh-keygen -Y sign -n hello@world -f id_ed25519 ed25519.txt \ No newline at end of file +ssh-keygen -Y sign -n hello@world -f testkeys/id_rsa testkeys/rsa.txt +ssh-keygen -Y sign -n hello@world -f testkeys/id_ecdsa testkeys/ecdsa.txt +ssh-keygen -Y sign -n hello@world -f testkeys/id_ed25519 testkeys/ed25519.txt \ No newline at end of file diff --git a/src/sshkey_tools/cert.py b/src/sshkey_tools/cert.py index dfda6d3..0b63997 100644 --- a/src/sshkey_tools/cert.py +++ b/src/sshkey_tools/cert.py @@ -97,7 +97,8 @@ def get(self, name: str, default=None): if field: if isinstance(field, type): return field.DEFAULT - return field.value + if getattr(field, "value", False): + return field.value return field def getattrs(self) -> tuple: diff --git a/src/sshkey_tools/signatures.py b/src/sshkey_tools/signatures.py index c5ea51c..8f0c2ed 100644 --- a/src/sshkey_tools/signatures.py +++ b/src/sshkey_tools/signatures.py @@ -53,16 +53,19 @@ class SSHSignature: """ General class for SSH Signatures, used for loading and parsing. """ + data: bytes = None + def __init__( self, signer_privkey: _KEY.PrivateKey = None, fields: SignatureFieldset = SignatureFieldset ): self.fields = fields() if isinstance(fields, type) else fields - if isinstance(signer_privkey, type) and signer_privkey is not None: + if signer_privkey is not None: self.fields.replace_field( "signature", _FIELD.SignatureField.from_object(signer_privkey) - ) + ) + self.fields.replace_field("public_key", signer_privkey.public_key) @classmethod def from_file(cls, path: str, encoding: str = 'none') -> "SSHSignature": @@ -137,7 +140,23 @@ def get_signable(self, data: Union[str, bytes]) -> bytes: ) return bytes(self.fields) + _FIELD.StringField.encode(hash) + + def get_signable_file(self, path: str) -> bytes: + """ + Loads the signable content from a file. + Will be loaded as bytes without encoding. + Args: + path (str): Path to the file + + Returns: + bytes: The signable data from the file + """ + with open(path, 'rb') as f: + data = f.read() + + return self.get_signable(data) + def __str__(self) -> str: table = PrettyTable(["Field", "Value"]) @@ -163,14 +182,19 @@ def verify( self, data, public_key: _KEY.PublicKey = None, raise_on_error: bool = False ) -> bool: if not public_key: - public_key = self.get('public_key').value + public_key = self.fields.get('public_key', None) public_key.verify( self.get_signable(data), self.fields.signature.value ) - - - - print() + + def sign(self, data: Union[str, bytes]): + signable = self.get_signable(data) + self.fields.signature.sign(signable) + + def sign_file(self, path: str): + signable = self.get_signable_file(path) + self.fields.signature.sign(signable) + \ No newline at end of file diff --git a/validate_signatures.py b/validate_signatures.py index 24330ef..6e0f557 100644 --- a/validate_signatures.py +++ b/validate_signatures.py @@ -5,23 +5,27 @@ signatures as _S ) +# Load public and private keys +rsa_priv = _K.PrivateKey.from_file('testkeys/id_rsa') +ecdsa_priv = _K.PrivateKey.from_file('testkeys/id_ecdsa') +ed25519_priv = _K.PrivateKey.from_file('testkeys/id_ed25519') +rsa_pub = rsa_priv.public_key +ecdsa_pub = ecdsa_priv.public_key +ed25519_pub = ed25519_priv.public_key -# Validate files created with ssh-keygen (WORKS!) -rsa_pub = _K.PublicKey.from_file('testkeys/id_rsa.pub') -ecdsa_pub = _K.PublicKey.from_file('testkeys/id_ecdsa.pub') -ed25519_pub = _K.PublicKey.from_file('testkeys/id_ed25519.pub') - +# Load externally created signatures rsa_sign = _S.SSHSignature.from_file('testkeys/rsa.txt.sig') ecdsa_sign = _S.SSHSignature.from_file('testkeys/ecdsa.txt.sig') ed25519_sign = _S.SSHSignature.from_file('testkeys/ed25519.txt.sig') -rsa_data = open('rsa.txt', 'rb').read() -ecdsa_data = open('ecdsa.txt', 'rb').read() -ed25519_data = open('ed25519.txt', 'rb').read() +# Load the data used for the signatures +rsa_data = open('testkeys/rsa.txt', 'rb').read() +ecdsa_data = open('testkeys/ecdsa.txt', 'rb').read() +ed25519_data = open('testkeys/ed25519.txt', 'rb').read() -rsa_signable = rsa_sign.get_signable(rsa_data) -ecdsa_signable = ecdsa_sign.get_signable(ecdsa_data) -ed25519_signable = ed25519_sign.get_signable(ed25519_data) +rsa_signable = rsa_sign.get_signable_file('testkeys/rsa.txt') +ecdsa_signable = ecdsa_sign.get_signable_file('testkeys/ecdsa.txt') +ed25519_signable = ed25519_sign.get_signable_file('testkeys/ed25519.txt') try: rsa_pub.verify(rsa_signable, rsa_sign.fields.signature.value) @@ -38,4 +42,25 @@ except: print("Ed25519 validation failed") -print() + + +try: + rsasig = _S.SSHSignature(rsa_priv) + rsasig.sign(rsa_data) + rsa_pub.verify(rsasig.get_signable(rsa_data), rsasig.fields.signature.value) +except: + print("RSA validation after signing failed") + +try: + ecdsasig = _S.SSHSignature(ecdsa_priv) + ecdsasig.sign(ecdsa_data) + ecdsa_pub.verify(ecdsasig.get_signable(ecdsa_data), ecdsasig.fields.signature.value) +except: + print("ECDSA validation after signing failed") + +try: + ed25519sig = _S.SSHSignature(ed25519_priv) + ed25519sig.sign(ed25519_data) + ed25519_pub.verify(ed25519sig.get_signable(ed25519_data), ed25519sig.fields.signature.value) +except: + print("Ed25519 validation after signing failed") From 5dc620e29ac060180c0235eacaaa488ea435d548 Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 1 Jan 2024 22:15:39 +0100 Subject: [PATCH 25/26] Saving signatures now works --- src/sshkey_tools/signatures.py | 49 +++++++++++++++++++++++++++++++--- validate_signatures.py | 13 ++++++--- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/sshkey_tools/signatures.py b/src/sshkey_tools/signatures.py index 8f0c2ed..88ba352 100644 --- a/src/sshkey_tools/signatures.py +++ b/src/sshkey_tools/signatures.py @@ -7,7 +7,7 @@ # string reserved # string hash_algorithm # string signature -from base64 import b64decode +from base64 import b64decode, b64encode from .cert import Fieldset, dataclass, Union from prettytable import PrettyTable from .utils import concat_to_bytestring, concat_to_string, ensure_bytestring @@ -48,6 +48,32 @@ def __bytes__(self): bytes(self.reserved), bytes(self.hash_algorithm) ) + + def format_pubkey(self): + pubkey = self.public_key.value.to_string().split(' ') + + return _FIELD.StringField.encode(concat_to_bytestring( + pubkey[0], + ' ', + b64decode(pubkey[1])) + ) + + + # return _FIELD.BytestringField.encode(concat_to_bytestring( + # _FIELD.StringField.encode(pubkey[0]), + # _FIELD.StringField.encode(b64decode(pubkey[1])) + # )) + + def bytes_out(self): + return concat_to_bytestring( + bytes(self.magic_preamble), + bytes(self.sig_version), + _FIELD.StringField.encode(self.public_key.value.raw_bytes()), + bytes(self.namespace), + bytes(self.reserved), + bytes(self.hash_algorithm), + bytes(self.signature) + ) class SSHSignature: """ @@ -65,8 +91,11 @@ def __init__( self.fields.replace_field( "signature", _FIELD.SignatureField.from_object(signer_privkey) ) - self.fields.replace_field("public_key", signer_privkey.public_key) + self.fields.replace_field("public_key", _FIELD.PublicKeyField.from_object(signer_privkey.public_key)) + if issubclass(type(self.fields.public_key.value), type(self.fields.public_key)): + self.fields.public_key = self.fields.public_key.value + @classmethod def from_file(cls, path: str, encoding: str = 'none') -> "SSHSignature": """ @@ -196,5 +225,17 @@ def sign(self, data: Union[str, bytes]): def sign_file(self, path: str): signable = self.get_signable_file(path) self.fields.signature.sign(signable) - - \ No newline at end of file + + def to_string(self, data): + content = self.fields.bytes_out() + content = b64encode(content) + file_content = b"-----BEGIN SSH SIGNATURE-----\n" + file_content += b''.join([content[i:i+70] + b"\n" for i in range(0, len(content), 70)]) + file_content += b"-----END SSH SIGNATURE-----" + + return file_content + + def to_file(self, data, path: str): + with open(path, 'wb') as f: + f.write(self.to_string(data)) + \ No newline at end of file diff --git a/validate_signatures.py b/validate_signatures.py index 6e0f557..4cba697 100644 --- a/validate_signatures.py +++ b/validate_signatures.py @@ -27,18 +27,23 @@ ecdsa_signable = ecdsa_sign.get_signable_file('testkeys/ecdsa.txt') ed25519_signable = ed25519_sign.get_signable_file('testkeys/ed25519.txt') -try: - rsa_pub.verify(rsa_signable, rsa_sign.fields.signature.value) -except: - print("RSA validation failed") +# try: +# ecdsa_pub.verify(ecdsa_signable, ecdsa_sign.fields.signature.value) +# ecdsa_pub.to_file('testkeys/ecdsa.txt.sig2') +rsa_pub.verify(rsa_signable, rsa_sign.fields.signature.value) +rsa_sign.to_file(rsa_data, 'testkeys/rsa.txt.sig2') +# except: + # print("RSA validation failed") try: ecdsa_pub.verify(ecdsa_signable, ecdsa_sign.fields.signature.value) + ecdsa_sign.to_file('testkeys/ecdsa.txt.sig2') except: print("ECDSA validation failed") try: ed25519_pub.verify(ed25519_signable, ed25519_sign.fields.signature.value) + ed25519_sign.to_file('testkeys/ed25519.txt.sig2') except: print("Ed25519 validation failed") From e0477baeeeea9da8aa1621cd57ca430d24717b35 Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 1 Jan 2024 22:20:56 +0100 Subject: [PATCH 26/26] Removed data parameter from to_string/to_file --- src/sshkey_tools/signatures.py | 6 +++--- validate_signatures.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sshkey_tools/signatures.py b/src/sshkey_tools/signatures.py index 88ba352..9b6aed2 100644 --- a/src/sshkey_tools/signatures.py +++ b/src/sshkey_tools/signatures.py @@ -226,7 +226,7 @@ def sign_file(self, path: str): signable = self.get_signable_file(path) self.fields.signature.sign(signable) - def to_string(self, data): + def to_string(self): content = self.fields.bytes_out() content = b64encode(content) file_content = b"-----BEGIN SSH SIGNATURE-----\n" @@ -235,7 +235,7 @@ def to_string(self, data): return file_content - def to_file(self, data, path: str): + def to_file(self, path: str): with open(path, 'wb') as f: - f.write(self.to_string(data)) + f.write(self.to_string()) \ No newline at end of file diff --git a/validate_signatures.py b/validate_signatures.py index 4cba697..225e6a3 100644 --- a/validate_signatures.py +++ b/validate_signatures.py @@ -31,7 +31,7 @@ # ecdsa_pub.verify(ecdsa_signable, ecdsa_sign.fields.signature.value) # ecdsa_pub.to_file('testkeys/ecdsa.txt.sig2') rsa_pub.verify(rsa_signable, rsa_sign.fields.signature.value) -rsa_sign.to_file(rsa_data, 'testkeys/rsa.txt.sig2') +rsa_sign.to_file('testkeys/rsa.txt.sig2') # except: # print("RSA validation failed")