1- # Format:
1+ """SSH Data signature implementation
2+
3+ Raises:
4+ _EX.InvalidDataException: Data is invalid
5+ _EX.InvalidHashAlgorithmException: The hash algorithm is invalid
6+ _EX.InvalidCertificateFieldException: The certificate field contains invalid data
7+ """
28
9+ # Format:
310# byte[6] MAGIC_PREAMBLE (SSHSIG)
411# uint32 SIG_VERSION (0x01)
512# string publickey
815# string hash_algorithm
916# string signature
1017from base64 import b64decode , b64encode
11- from .cert import Fieldset , dataclass , Union
1218from prettytable import PrettyTable
19+ from .cert import Fieldset , dataclass , Union
1320from .utils import concat_to_bytestring , ensure_bytestring
1421from . import fields as _FIELD , keys as _KEY , exceptions as _EX , utils as _U
1522
@@ -40,6 +47,11 @@ class SignatureFieldset(Fieldset):
4047 signature : _FIELD .SignatureField = _FIELD .SignatureField .factory
4148
4249 def __bytes__ (self ):
50+ """Returns the data as a bytestring for encoding
51+
52+ Returns:
53+ data: Data bytes
54+ """
4355 return concat_to_bytestring (
4456 bytes (self .magic_preamble ),
4557 bytes (self .namespace ),
@@ -48,6 +60,11 @@ def __bytes__(self):
4860 )
4961
5062 def format_pubkey (self ):
63+ """Format the public key and strip and extra data
64+
65+ Returns:
66+ key: The formatted public key
67+ """
5168 pubkey = self .public_key .value .to_string ().split (" " )
5269
5370 return _FIELD .StringField .encode (
@@ -60,6 +77,11 @@ def format_pubkey(self):
6077 # ))
6178
6279 def bytes_out (self ):
80+ """Generates the output bytes for the finished signature
81+
82+ Returns:
83+ output: The complete signature
84+ """
6385 return concat_to_bytestring (
6486 bytes (self .magic_preamble ),
6587 bytes (self .sig_version ),
@@ -155,7 +177,8 @@ def from_file(cls, path: str, encoding: str = "none") -> "SSHSignature":
155177
156178 Args:
157179 path (str): The path to the file
158- encoding (str, optional): The encoding of the file. None will load the byte content directly. Defaults to 'utf-8'.
180+ encoding (str, optional): The encoding of the file. None will load the byte
181+ content directly. Defaults to 'utf-8'.
159182
160183 Returns:
161184 SSHSignature: SSH Signature Object
@@ -212,17 +235,17 @@ def get_signable(self, data: Union[str, bytes]) -> bytes:
212235 Returns:
213236 bytes: The signable data
214237 """
215- hash = b""
238+ hash_kind = b""
216239 if self .fields .hash_algorithm .value == "sha256" :
217- hash = _U .sha256_hash (ensure_bytestring (data ))
240+ hash_kind = _U .sha256_hash (ensure_bytestring (data ))
218241 elif self .fields .hash_algorithm .value == "sha512" :
219- hash = _U .sha512_hash (ensure_bytestring (data ))
242+ hash_kind = _U .sha512_hash (ensure_bytestring (data ))
220243 else :
221244 raise _EX .InvalidHashAlgorithmException (
222245 f"Unknown hash algorithm { self .fields .hash_algorithm } "
223246 )
224247
225- return bytes (self .fields ) + _FIELD .StringField .encode (hash )
248+ return bytes (self .fields ) + _FIELD .StringField .encode (hash_kind )
226249
227250 def get_signable_file (self , path : str ) -> bytes :
228251 """
@@ -249,12 +272,32 @@ def __str__(self) -> str:
249272 return str (table )
250273
251274 def get (self , field : str ):
275+ """Get the contents of a field
276+
277+ Args:
278+ field (str): Field name
279+
280+ Raises:
281+ _EX.InvalidCertificateFieldException: Invalid field name
282+
283+ Returns:
284+ data: The field data
285+ """
252286 if field in self .fields .getattrs ():
253287 return self .fields .get (field , None )
254288
255289 raise _EX .InvalidCertificateFieldException (f"Unknown field { field } " )
256290
257291 def set (self , field : str , data ):
292+ """Sets the contents of a field
293+
294+ Args:
295+ field (str): Field name
296+ data (any): Data for the field
297+
298+ Raises:
299+ _EX.InvalidCertificateFieldException: Invalid field name
300+ """
258301 if field in self .fields .getattrs ():
259302 self .fields .set (field , data )
260303 return
@@ -264,20 +307,53 @@ def set(self, field: str, data):
264307 def verify (
265308 self , data , public_key : _KEY .PublicKey = None , raise_on_error : bool = False
266309 ) -> bool :
310+ """Verify a SSH Signature
311+
312+ Args:
313+ data (bytes): The signed data
314+ public_key (_KEY.PublicKey, optional): The public key to use to verify
315+ the signature. If none are specified, the public key from the
316+ signature will be used. Defaults to None.
317+ raise_on_error (bool, optional): Whether to raise an exception on
318+ verification failure. Defaults to False.
319+
320+ Returns:
321+ bool: True if the signature is valid, False otherwise.
322+ """
267323 if not public_key :
268324 public_key = self .fields .get ("public_key" , None )
269325
326+ if raise_on_error and not public_key .verify (
327+ self .get_signable (data ), self .fields .signature .value
328+ ):
329+ raise _EX .InvalidSignatureException ("Signature verification failed" )
330+
270331 public_key .verify (self .get_signable (data ), self .fields .signature .value )
271332
272333 def sign (self , data : Union [str , bytes ]):
334+ """Signs the data
335+
336+ Args:
337+ data (Union[str, bytes]): The data to sign
338+ """
273339 signable = self .get_signable (data )
274340 self .fields .signature .sign (signable )
275341
276342 def sign_file (self , path : str ):
343+ """Signs a file at a given path
344+
345+ Args:
346+ path (str): The path to the file to sign
347+ """
277348 signable = self .get_signable_file (path )
278349 self .fields .signature .sign (signable )
279350
280351 def to_string (self ):
352+ """Converts the signature data to the text-based signature format
353+
354+ Returns:
355+ string: The encoded signature data
356+ """
281357 content = self .fields .bytes_out ()
282358 content = b64encode (content )
283359 file_content = b"-----BEGIN SSH SIGNATURE-----\n "
@@ -289,5 +365,10 @@ def to_string(self):
289365 return file_content
290366
291367 def to_file (self , path : str ):
368+ """Save the content of the signature to a file
369+
370+ Args:
371+ path (str): The path to the file
372+ """
292373 with open (path , "wb" ) as f :
293374 f .write (self .to_string ())
0 commit comments