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")