diff options
-rw-r--r-- | paramiko/ed25519key.py | 24 | ||||
-rw-r--r-- | paramiko/pkey.py | 36 | ||||
-rw-r--r-- | tests/test_pkey.py | 4 | ||||
-rw-r--r-- | tests/test_rsa_openssh_nopad.key | 27 |
4 files changed, 56 insertions, 35 deletions
diff --git a/paramiko/ed25519key.py b/paramiko/ed25519key.py index 96cff7d0..b584f521 100644 --- a/paramiko/ed25519key.py +++ b/paramiko/ed25519key.py @@ -21,32 +21,12 @@ from cryptography.hazmat.primitives.ciphers import Cipher import nacl.signing -import six - from paramiko.message import Message -from paramiko.pkey import PKey +from paramiko.pkey import PKey, OPENSSH_AUTH_MAGIC, _unpad_openssh from paramiko.py3compat import b from paramiko.ssh_exception import SSHException, PasswordRequiredException -OPENSSH_AUTH_MAGIC = b"openssh-key-v1\x00" - - -def unpad(data): - # At the moment, this is only used for unpadding private keys on disk. This - # really ought to be made constant time (possibly by upstreaming this logic - # into pyca/cryptography). - padding_length = six.indexbytes(data, -1) - if 0x20 <= padding_length < 0x7f: - return data # no padding, last byte part comment (printable ascii) - if padding_length > 15: - raise SSHException("Invalid key") - for i in range(padding_length): - if six.indexbytes(data, i - padding_length) != i + 1: - raise SSHException("Invalid key") - return data[:-padding_length] - - class Ed25519Key(PKey): """ Representation of an `Ed25519 <https://ed25519.cr.yp.to/>`_ key. @@ -155,7 +135,7 @@ class Ed25519Key(PKey): decryptor.update(private_ciphertext) + decryptor.finalize() ) - message = Message(unpad(private_data)) + message = Message(_unpad_openssh(private_data)) if message.get_int() != message.get_int(): raise SSHException("Invalid key") diff --git a/paramiko/pkey.py b/paramiko/pkey.py index c6beef51..3a07426f 100644 --- a/paramiko/pkey.py +++ b/paramiko/pkey.py @@ -27,6 +27,7 @@ from hashlib import md5 import re import struct +import six import bcrypt from cryptography.hazmat.backends import default_backend @@ -35,18 +36,29 @@ from cryptography.hazmat.primitives.ciphers import algorithms, modes, Cipher from paramiko import util from paramiko.common import o600 -from paramiko.py3compat import ( - u, - encodebytes, - decodebytes, - b, - string_types, - byte_ord, -) +from paramiko.py3compat import u, b, encodebytes, decodebytes, string_types from paramiko.ssh_exception import SSHException, PasswordRequiredException from paramiko.message import Message +OPENSSH_AUTH_MAGIC = b"openssh-key-v1\x00" + + +def _unpad_openssh(data): + # At the moment, this is only used for unpadding private keys on disk. This + # really ought to be made constant time (possibly by upstreaming this logic + # into pyca/cryptography). + padding_length = six.indexbytes(data, -1) + if 0x20 <= padding_length < 0x7f: + return data # no padding, last byte part comment (printable ascii) + if padding_length > 15: + raise SSHException("Invalid key") + for i in range(padding_length): + if six.indexbytes(data, i - padding_length) != i + 1: + raise SSHException("Invalid key") + return data[:-padding_length] + + class PKey(object): """ Base class for public keys. @@ -395,8 +407,8 @@ class PKey(object): raise SSHException("base64 decoding error: {}".format(e)) # read data struct - auth_magic = data[:14] - if auth_magic != b("openssh-key-v1"): + auth_magic = data[:15] + if auth_magic != OPENSSH_AUTH_MAGIC: raise SSHException("unexpected OpenSSH key header encountered") cstruct = self._uint32_cstruct_unpack(data[15:], "sssur") @@ -466,9 +478,7 @@ class PKey(object): "OpenSSH private key file checkints do not match" ) - # Remove padding - padlen = byte_ord(keydata[len(keydata) - 1]) - return keydata[: len(keydata) - padlen] + return _unpad_openssh(keydata) def _uint32_cstruct_unpack(self, data, strformat): """ diff --git a/tests/test_pkey.py b/tests/test_pkey.py index 17893ca2..8d88545a 100644 --- a/tests/test_pkey.py +++ b/tests/test_pkey.py @@ -494,6 +494,10 @@ class KeyTest(unittest.TestCase): finally: os.remove(newfile) + def test_load_openssh_format_RSA_nopad(self): + # check just not exploding with 'Invalid key' + RSAKey.from_private_key_file(_support("test_rsa_openssh_nopad.key")) + def test_stringification(self): key = RSAKey.from_private_key_file(_support("test_rsa.key")) comparable = TEST_KEY_BYTESTR_2 if PY2 else TEST_KEY_BYTESTR_3 diff --git a/tests/test_rsa_openssh_nopad.key b/tests/test_rsa_openssh_nopad.key new file mode 100644 index 00000000..61ac1b19 --- /dev/null +++ b/tests/test_rsa_openssh_nopad.key @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAnyMwWSwrbJxxQZWMJO5xR6eAA9De4t3GViqDRaQt/BgsvzZ14SUz +aOL/A370fKxhx/JLIOOGA0o5B0/ct+CL7XFqMi5r5+iA9VcIeYKKtoAkrEvRnagNW0WVWx +thTnE01g8Pb7fDqzI2cBuBNZ2vGNm2m4UTGC8/kl/0ES1V3KqA7lPlTrkTYg9L/ornvVHc +c8gEbMwx9XXVRzbWiuDE176ojrudY9CZduVSOgW+HK3rKkqLBs/91jv0zUK0oqTQBLR7E2 +V2GWPDU4BjlHTtYr0jpKOGDr1DLu4+NiD/mX+tGMdH6ehbDii0kXmOUaZjs4OxuK3XA/gi +KZLdj1jQQwAAA7iNnvAVjZ7wFQAAAAdzc2gtcnNhAAABAQCfIzBZLCtsnHFBlYwk7nFHp4 +AD0N7i3cZWKoNFpC38GCy/NnXhJTNo4v8DfvR8rGHH8ksg44YDSjkHT9y34IvtcWoyLmvn +6ID1Vwh5goq2gCSsS9GdqA1bRZVbG2FOcTTWDw9vt8OrMjZwG4E1na8Y2babhRMYLz+SX/ +QRLVXcqoDuU+VOuRNiD0v+iue9UdxzyARszDH1ddVHNtaK4MTXvqiOu51j0Jl25VI6Bb4c +resqSosGz/3WO/TNQrSipNAEtHsTZXYZY8NTgGOUdO1ivSOko4YOvUMu7j42IP+Zf60Yx0 +fp6FsOKLSReY5RpmOzg7G4rdcD+CIpkt2PWNBDAAAAAwEAAQAAAQEAnmMbn+VCYxth7fC2 +R5u6y6J+201sSUiKOwCdHxdFXX+CKd4+fRPVkzM6tXQKSnwX5jXVaKqLm4KoOArYl3q6Sl +1zYParF2plz8oL+URgYzwvQ/1CaDP29zzOZptdwgESoWrj5kF0UlPrsrDtbTvAJm+qPCe6 +1XtRPpKaDO6eYr0PM2QTElZy3mDBUBvu816LdG/ZtnB9g5UsocT5mmhpHTHdjrpwNu5TBe +ACVodDn5Fu66OlrrnQi4IPCAWKJ1YuzEkZqLhs1L3oMHACsmzrLjzW74SjY4kWTTvGiC6i +tDoycycThk9EGLGNso99Q1fe84/OZUff7aI3yK9KvLL7oQAAAIEAh2+XrJXSBx/v9E3aJH +ncgQH1snXr7LcSRqcWicHdbm8JsOTT3TkyXHGlSZ2rr/Y0u5V1ZSO6roJLrAHsDJzx0x0U +xE/5mpzhD+yIKQwnWkZFLzYEnYDFdXDMzmghUIik9AW7n9dtS8UtVFGaL6Vs2YCOuLqeT9 +nZUkm3UUZ+7QIAAACBAM23DFjQ0/Op2ri7fJA2qFBdXqoJdNHuyYEIrKbB6XaaSUz52+IB +MbccxEz3vPsHh69tZoJ+xZNbFJe9wdmbF+DQpoukHkJnzpk/pUq8LjQMzZfwv41X8zqaq4 +AOA7g27Rk8aKewhCXjhkr0hHEaSiuqIIindFaFti5sQMi2mtkXAAAAgQDGCXkpuKZK61p9 +L6G5yZSQBCgVtm0iQEbyDXWHjy/GqLtxJjqdyaRK57hXGjbzgJJraSy+sNP9uv2QOvyZvB +3XaPWwUYVQ34WyibCqqUaPiHxX7T1lZV+asbwgbmSqYtH5dUEJ8zT572mCwxnRjX63PwDo +5vBbR/qAW5lvRYsltQAAAAFh +-----END OPENSSH PRIVATE KEY----- |