From 7a0aaa57736fb833033c86aa0d66301d775efca0 Mon Sep 17 00:00:00 2001
From: Leite Machado <jorge.leite-machado@etu.hesge.ch>
Date: Fri, 26 May 2023 20:59:15 +0200
Subject: [PATCH] Deporting ssh signature by tweaking paramiko source code

---
 .gitignore                                    |    1 +
 auth.py                                       |  141 +
 client.py                                     |  109 +
 gen_keys.py                                   |   28 +
 paramiko/__init__.py                          |  143 +
 paramiko/__pycache__/__init__.cpython-311.pyc |  Bin 0 -> 4197 bytes
 paramiko/__pycache__/_version.cpython-311.pyc |  Bin 0 -> 362 bytes
 paramiko/__pycache__/agent.cpython-311.pyc    |  Bin 0 -> 22508 bytes
 .../__pycache__/auth_handler.cpython-311.pyc  |  Bin 0 -> 47384 bytes
 paramiko/__pycache__/ber.cpython-311.pyc      |  Bin 0 -> 6716 bytes
 .../__pycache__/buffered_pipe.cpython-311.pyc |  Bin 0 -> 9686 bytes
 paramiko/__pycache__/channel.cpython-311.pyc  |  Bin 0 -> 66597 bytes
 paramiko/__pycache__/client.cpython-311.pyc   |  Bin 0 -> 34752 bytes
 paramiko/__pycache__/common.cpython-311.pyc   |  Bin 0 -> 7992 bytes
 paramiko/__pycache__/compress.cpython-311.pyc |  Bin 0 -> 1743 bytes
 paramiko/__pycache__/config.cpython-311.pyc   |  Bin 0 -> 25965 bytes
 paramiko/__pycache__/dsskey.cpython-311.pyc   |  Bin 0 -> 12015 bytes
 paramiko/__pycache__/ecdsakey.cpython-311.pyc |  Bin 0 -> 16430 bytes
 .../__pycache__/ed25519key.cpython-311.pyc    |  Bin 0 -> 9155 bytes
 paramiko/__pycache__/file.cpython-311.pyc     |  Bin 0 -> 21478 bytes
 paramiko/__pycache__/hostkeys.cpython-311.pyc |  Bin 0 -> 19771 bytes
 .../kex_curve25519.cpython-311.pyc            |  Bin 0 -> 8386 bytes
 .../__pycache__/kex_ecdh_nist.cpython-311.pyc |  Bin 0 -> 9198 bytes
 paramiko/__pycache__/kex_gex.cpython-311.pyc  |  Bin 0 -> 14430 bytes
 .../__pycache__/kex_group1.cpython-311.pyc    |  Bin 0 -> 7998 bytes
 .../__pycache__/kex_group14.cpython-311.pyc   |  Bin 0 -> 1402 bytes
 .../__pycache__/kex_group16.cpython-311.pyc   |  Bin 0 -> 1397 bytes
 paramiko/__pycache__/kex_gss.cpython-311.pyc  |  Bin 0 -> 32255 bytes
 paramiko/__pycache__/message.cpython-311.pyc  |  Bin 0 -> 13496 bytes
 paramiko/__pycache__/packet.cpython-311.pyc   |  Bin 0 -> 25065 bytes
 paramiko/__pycache__/pipe.cpython-311.pyc     |  Bin 0 -> 6790 bytes
 paramiko/__pycache__/pkey.cpython-311.pyc     |  Bin 0 -> 33030 bytes
 paramiko/__pycache__/primes.cpython-311.pyc   |  Bin 0 -> 4915 bytes
 paramiko/__pycache__/proxy.cpython-311.pyc    |  Bin 0 -> 5414 bytes
 paramiko/__pycache__/rsakey.cpython-311.pyc   |  Bin 0 -> 10053 bytes
 paramiko/__pycache__/server.cpython-311.pyc   |  Bin 0 -> 34312 bytes
 paramiko/__pycache__/sftp.cpython-311.pyc     |  Bin 0 -> 7975 bytes
 .../__pycache__/sftp_attr.cpython-311.pyc     |  Bin 0 -> 10463 bytes
 .../__pycache__/sftp_client.cpython-311.pyc   |  Bin 0 -> 45270 bytes
 .../__pycache__/sftp_file.cpython-311.pyc     |  Bin 0 -> 26361 bytes
 .../__pycache__/sftp_handle.cpython-311.pyc   |  Bin 0 -> 8588 bytes
 .../__pycache__/sftp_server.cpython-311.pyc   |  Bin 0 -> 26615 bytes
 paramiko/__pycache__/sftp_si.cpython-311.pyc  |  Bin 0 -> 13863 bytes
 .../__pycache__/ssh_exception.cpython-311.pyc |  Bin 0 -> 10342 bytes
 paramiko/__pycache__/ssh_gss.cpython-311.pyc  |  Bin 0 -> 29820 bytes
 .../__pycache__/transport.cpython-311.pyc     |  Bin 0 -> 117148 bytes
 paramiko/__pycache__/util.cpython-311.pyc     |  Bin 0 -> 14990 bytes
 paramiko/_version.py                          |    2 +
 paramiko/_winapi.py                           |  413 +++
 paramiko/agent.py                             |  450 +++
 paramiko/auth_handler.py                      | 1056 ++++++
 paramiko/ber.py                               |  139 +
 paramiko/buffered_pipe.py                     |  212 ++
 paramiko/channel.py                           | 1390 +++++++
 paramiko/client.py                            |  865 +++++
 paramiko/common.py                            |  248 ++
 paramiko/compress.py                          |   40 +
 paramiko/config.py                            |  679 ++++
 paramiko/dsskey.py                            |  255 ++
 paramiko/ecdsakey.py                          |  333 ++
 paramiko/ed25519key.py                        |  209 ++
 paramiko/file.py                              |  528 +++
 paramiko/hostkeys.py                          |  389 ++
 paramiko/kex_curve25519.py                    |  131 +
 paramiko/kex_ecdh_nist.py                     |  151 +
 paramiko/kex_gex.py                           |  288 ++
 paramiko/kex_group1.py                        |  155 +
 paramiko/kex_group14.py                       |   40 +
 paramiko/kex_group16.py                       |   35 +
 paramiko/kex_gss.py                           |  686 ++++
 paramiko/message.py                           |  318 ++
 paramiko/packet.py                            |  634 ++++
 paramiko/pipe.py                              |  148 +
 paramiko/pkey.py                              |  747 ++++
 paramiko/primes.py                            |  148 +
 paramiko/proxy.py                             |  134 +
 paramiko/rsakey.py                            |  217 ++
 paramiko/server.py                            |  732 ++++
 paramiko/sftp.py                              |  224 ++
 paramiko/sftp_attr.py                         |  239 ++
 paramiko/sftp_client.py                       |  930 +++++
 paramiko/sftp_file.py                         |  570 +++
 paramiko/sftp_handle.py                       |  196 +
 paramiko/sftp_server.py                       |  537 +++
 paramiko/sftp_si.py                           |  316 ++
 paramiko/ssh_exception.py                     |  235 ++
 paramiko/ssh_gss.py                           |  778 ++++
 paramiko/transport.py                         | 3217 +++++++++++++++++
 paramiko/util.py                              |  337 ++
 paramiko/win_openssh.py                       |   56 +
 paramiko/win_pageant.py                       |  138 +
 ssh-keys/hello.txt                            |    2 +
 ssh-keys/hello2.txt                           |    0
 ssh-keys/test                                 |   39 +
 ssh-keys/test.pub                             |    1 +
 ssh-keys/yeeee                                |   39 +
 ssh-keys/yeeee.pub                            |    1 +
 97 files changed, 20049 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 auth.py
 create mode 100644 client.py
 create mode 100644 gen_keys.py
 create mode 100644 paramiko/__init__.py
 create mode 100644 paramiko/__pycache__/__init__.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/_version.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/agent.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/auth_handler.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/ber.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/buffered_pipe.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/channel.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/client.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/common.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/compress.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/config.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/dsskey.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/ecdsakey.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/ed25519key.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/file.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/hostkeys.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/kex_curve25519.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/kex_ecdh_nist.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/kex_gex.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/kex_group1.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/kex_group14.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/kex_group16.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/kex_gss.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/message.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/packet.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/pipe.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/pkey.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/primes.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/proxy.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/rsakey.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/server.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/sftp.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/sftp_attr.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/sftp_client.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/sftp_file.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/sftp_handle.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/sftp_server.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/sftp_si.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/ssh_exception.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/ssh_gss.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/transport.cpython-311.pyc
 create mode 100644 paramiko/__pycache__/util.cpython-311.pyc
 create mode 100644 paramiko/_version.py
 create mode 100644 paramiko/_winapi.py
 create mode 100644 paramiko/agent.py
 create mode 100644 paramiko/auth_handler.py
 create mode 100644 paramiko/ber.py
 create mode 100644 paramiko/buffered_pipe.py
 create mode 100644 paramiko/channel.py
 create mode 100644 paramiko/client.py
 create mode 100644 paramiko/common.py
 create mode 100644 paramiko/compress.py
 create mode 100644 paramiko/config.py
 create mode 100644 paramiko/dsskey.py
 create mode 100644 paramiko/ecdsakey.py
 create mode 100644 paramiko/ed25519key.py
 create mode 100644 paramiko/file.py
 create mode 100644 paramiko/hostkeys.py
 create mode 100644 paramiko/kex_curve25519.py
 create mode 100644 paramiko/kex_ecdh_nist.py
 create mode 100644 paramiko/kex_gex.py
 create mode 100644 paramiko/kex_group1.py
 create mode 100644 paramiko/kex_group14.py
 create mode 100644 paramiko/kex_group16.py
 create mode 100644 paramiko/kex_gss.py
 create mode 100644 paramiko/message.py
 create mode 100644 paramiko/packet.py
 create mode 100644 paramiko/pipe.py
 create mode 100644 paramiko/pkey.py
 create mode 100644 paramiko/primes.py
 create mode 100644 paramiko/proxy.py
 create mode 100644 paramiko/rsakey.py
 create mode 100644 paramiko/server.py
 create mode 100644 paramiko/sftp.py
 create mode 100644 paramiko/sftp_attr.py
 create mode 100644 paramiko/sftp_client.py
 create mode 100644 paramiko/sftp_file.py
 create mode 100644 paramiko/sftp_handle.py
 create mode 100644 paramiko/sftp_server.py
 create mode 100644 paramiko/sftp_si.py
 create mode 100644 paramiko/ssh_exception.py
 create mode 100644 paramiko/ssh_gss.py
 create mode 100644 paramiko/transport.py
 create mode 100644 paramiko/util.py
 create mode 100644 paramiko/win_openssh.py
 create mode 100644 paramiko/win_pageant.py
 create mode 100644 ssh-keys/hello.txt
 create mode 100644 ssh-keys/hello2.txt
 create mode 100644 ssh-keys/test
 create mode 100644 ssh-keys/test.pub
 create mode 100644 ssh-keys/yeeee
 create mode 100644 ssh-keys/yeeee.pub

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..191611f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+./ssh-keys/*
\ No newline at end of file
diff --git a/auth.py b/auth.py
new file mode 100644
index 0000000..e60dd5f
--- /dev/null
+++ b/auth.py
@@ -0,0 +1,141 @@
+import socket
+import sys
+import json
+import os
+import base64
+
+
+# ajouter le chemin vers le répertoire libs à PYTHONPATH
+libs_path = os.path.join(os.getcwd(), 'libs')
+if libs_path not in sys.path:
+    sys.path.append(libs_path)
+
+# maintenant, vous pouvez importer votre bibliothèque
+import paramiko
+
+
+def _get_key_type_and_bits_o(key):
+    print("_get_key_type_and_bits")
+    """
+    Given any key, return its type/algorithm & bits-to-sign.
+
+    Intended for input to or verification of, key signatures.
+    """
+    # Use certificate contents, if available, plain pubkey otherwise
+    if key.public_blob:
+        return key.public_blob.key_type, key.public_blob.key_blob
+    else:
+        return key.get_name(), key
+
+def _get_session_blob(key, service, username, algorithm, session_id, userauth_request):
+        print("_get_session_blob ********************************")
+        m = paramiko.Message()
+        m.add_string(session_id)
+        # problématique car comment avoir la session_id avant de s'authentifier ?
+        # print("session_id: ", self.transport.session_id)
+        m.add_byte(userauth_request)
+        # print("cMSG_USERAUTH_REQUEST", cMSG_USERAUTH_REQUEST)
+        m.add_string(username)
+        print("username: ", username)
+        m.add_string(service)
+        print("service: ", service)
+        m.add_string("publickey")
+        m.add_boolean(True)
+        _, bits = _get_key_type_and_bits_o(key)
+        m.add_string(algorithm)
+        m.add_string(bits)
+        print("*****************************")
+        return m.asbytes()
+
+# Variables pour le serveur de signature
+hostname = 'localhost'
+port = 12340
+
+# Variables pour la clé privée
+private_key_file = 'ssh-keys/test'
+password = None
+
+# Lire la clé privée au format OpenSSH
+privateKey = paramiko.RSAKey.from_private_key_file(private_key_file)
+
+# Démarrer le serveur de signatu
+signer_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+signer_server.bind((hostname, port))
+signer_server.listen(1)
+
+while True:
+    print("En attente d'une connexion...")
+    # Attendre une connexion
+    client, address = signer_server.accept()
+    print("Connexion recue !")
+    # print("Adresse :", address)
+    # print("Client :", client)
+    # Lire le défi
+    data = client.recv(1024)
+
+    info_dict = json.loads(data.decode())
+
+    # Vous pouvez maintenant accéder aux informations dans info_dict
+    algorithm = info_dict['algorithm']
+    session_id = base64.b64decode(info_dict['session_id'])
+    userauth_request = base64.b64decode(info_dict['userauth_request'])
+    username = info_dict['username']
+
+    m = paramiko.Message()
+
+
+    print("algorithm: ", algorithm)
+    print("session_id: ", session_id)
+    print("userauth_request: ", userauth_request)
+    print("username: ", username)
+
+    m.add_byte(userauth_request)
+    m.add_string(username)
+    m.add_string("ssh-connection")
+    m.add_string("publickey")
+    m.add_boolean(True)
+    
+
+    sessionblob = _get_session_blob(privateKey, "ssh-connection", username, algorithm, session_id, userauth_request)
+
+    keyType, bits = _get_key_type_and_bits_o(privateKey)
+
+    m.add_string("rsa-sha2-512")
+    m.add_string(bits)
+
+    print("sessionblob: ", sessionblob)
+
+    sig = privateKey.sign_ssh_data(sessionblob, algorithm)
+    print("signature: ", sig)
+
+    m.add_string(sig)
+
+    # info_dict_retour = {
+    #     'signature': base64.b64encode(sig.asbytes()).decode(),
+    #     'key_type': keyType,
+    #     'algorithm': algorithm,
+    #     'bits': base64.b64encode(bits.asbytes()).decode(),
+    # }
+    # print("bits decode: ", base64.b64encode(bits).decode().encode())
+    # # print("Défi reçu :", challenge)
+
+    # if challenge != b'':
+    #     print("Défi reçu :", challenge)
+    # Signer le défi
+
+    # Envoyer la signature au client
+    # sig_bytes = sig.asbytes()
+    try:
+        # Convertir le message en bytes
+        message_bytes = m.asbytes()
+
+        # Envoyer le message
+        client.sendall(message_bytes)
+
+    finally:
+        # Fermer la connexion
+        client.close()
+        # print(json.dumps(info_dict_retour).encode())
+    # client.send(json.dumps(info_dict_retour).encode())
+
+    client.close()
diff --git a/client.py b/client.py
new file mode 100644
index 0000000..0c8d5c1
--- /dev/null
+++ b/client.py
@@ -0,0 +1,109 @@
+import socket
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.backends import default_backend
+
+import sys
+import os
+
+
+# ajouter le chemin vers le répertoire libs à PYTHONPATH
+libs_path = os.path.join(os.getcwd(), 'libs')
+if libs_path not in sys.path:
+    sys.path.append(libs_path)
+
+# maintenant, vous pouvez importer votre bibliothèque
+import paramiko
+from paramiko.auth_handler import AuthHandler
+import logging
+# 
+# logging.basicConfig(level=logging.DEBUG)
+# logging.basicConfig(level=logging.ERROR)
+
+
+
+
+
+algorithm = "rsa-sha2-512"
+
+
+
+# Variables pour la connexion SSH
+hostname = '172.20.10.4'
+username = 'parallels'
+port = 22
+
+# Variables pour la connexion au deuxième dispositif
+signer_hostname = 'localhost'
+signer_port = 12340
+
+# Variables pour la clé publique
+public_key_file = 'ssh-keys/test.pub'
+
+# Lire la clé publique au format OpenSSH
+with open(public_key_file, 'rb') as key_file:
+    public_key = serialization.load_ssh_public_key(key_file.read(), backend=default_backend())
+
+public_key_pem = public_key.public_bytes(
+    encoding=serialization.Encoding.OpenSSH,
+    format=serialization.PublicFormat.OpenSSH
+)
+
+print("Contenu de la clé publique :\n", public_key_pem.decode())
+print("Clé publique chargée :", public_key)
+
+# Créer une nouvelle instance de transport
+transport = paramiko.Transport((hostname, port))
+
+# Créer une nouvelle instance de client de signature
+clePriv = paramiko.RSAKey.from_private_key_file('ssh-keys/test')
+
+
+# Essayer d'ouvrir la session SSH
+def handler(title, instructions, prompt_list):
+    print("*" * 80)
+    print("Handeling interactive authentication")
+    print("*" * 80)
+    signer_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    signer_client.connect((signer_hostname, signer_port))
+
+    resp = []
+    print("Prompt list :", prompt_list)
+    print("Instructions :", instructions)
+    print("Title :", title)
+    for pr in prompt_list:
+        print(pr)
+        if pr[0].startswith('Password'):
+
+            # Transmettre le défi au deuxième dispositif
+
+            print("Envoi du défi : ", pr[0].encode())
+
+            signer_client.sendall(pr[0].encode())
+
+            # Attendre la réponse du deuxième dispositif
+            signed_challenge = signer_client.recv(1024)
+
+            print("Défi signé :", signed_challenge)
+            # resp.append("Jdlm1209") 
+            resp.append(signed_challenge)
+        else:
+            resp.append('')
+    return resp
+
+
+print("Connexion au serveur SSH...")
+transport.connect()
+
+print("Connexion réussie !")
+print("Authentification for username {}...".format(username))
+
+# transport.auth_publickey(username, public_key, handler)
+
+try:
+    transport.auth_publickey(username, public_key)
+except paramiko.ssh_exception.AuthenticationException:
+    print("Échec de l'authentification")
+else:
+    print("Authentification réussie !")
+
+    
diff --git a/gen_keys.py b/gen_keys.py
new file mode 100644
index 0000000..8cfdd3d
--- /dev/null
+++ b/gen_keys.py
@@ -0,0 +1,28 @@
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.hazmat.backends import default_backend
+
+# Génération de la clé privée
+private_key = rsa.generate_private_key(
+    public_exponent=65537,
+    key_size=2048,
+    backend=default_backend()
+)
+
+# Sauvegarde de la clé privée au format PEM
+with open('private_key.pem', 'wb') as f:
+    f.write(private_key.private_bytes(
+        encoding=serialization.Encoding.PEM,
+        format=serialization.PrivateFormat.PKCS8,
+        encryption_algorithm=serialization.NoEncryption()
+    ))
+
+# Génération de la clé publique
+public_key = private_key.public_key()
+
+# Sauvegarde de la clé publique au format PEM
+with open('public_key.pem', 'wb') as f:
+    f.write(public_key.public_bytes(
+        encoding=serialization.Encoding.PEM,
+        format=serialization.PublicFormat.SubjectPublicKeyInfo
+    ))
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
new file mode 100644
index 0000000..cbc240a
--- /dev/null
+++ b/paramiko/__init__.py
@@ -0,0 +1,143 @@
+# Copyright (C) 2003-2011  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+# flake8: noqa
+import sys
+from paramiko._version import __version__, __version_info__
+from paramiko.transport import SecurityOptions, Transport
+from paramiko.client import (
+    SSHClient,
+    MissingHostKeyPolicy,
+    AutoAddPolicy,
+    RejectPolicy,
+    WarningPolicy,
+)
+from paramiko.auth_handler import AuthHandler
+from paramiko.ssh_gss import GSSAuth, GSS_AUTH_AVAILABLE, GSS_EXCEPTIONS
+from paramiko.channel import (
+    Channel,
+    ChannelFile,
+    ChannelStderrFile,
+    ChannelStdinFile,
+)
+from paramiko.ssh_exception import (
+    AuthenticationException,
+    BadAuthenticationType,
+    BadHostKeyException,
+    ChannelException,
+    ConfigParseError,
+    CouldNotCanonicalize,
+    IncompatiblePeer,
+    PasswordRequiredException,
+    ProxyCommandFailure,
+    SSHException,
+)
+from paramiko.server import ServerInterface, SubsystemHandler, InteractiveQuery
+from paramiko.rsakey import RSAKey
+from paramiko.dsskey import DSSKey
+from paramiko.ecdsakey import ECDSAKey
+from paramiko.ed25519key import Ed25519Key
+from paramiko.sftp import SFTPError, BaseSFTP
+from paramiko.sftp_client import SFTP, SFTPClient
+from paramiko.sftp_server import SFTPServer
+from paramiko.sftp_attr import SFTPAttributes
+from paramiko.sftp_handle import SFTPHandle
+from paramiko.sftp_si import SFTPServerInterface
+from paramiko.sftp_file import SFTPFile
+from paramiko.message import Message
+from paramiko.packet import Packetizer
+from paramiko.file import BufferedFile
+from paramiko.agent import Agent, AgentKey
+from paramiko.pkey import PKey, PublicBlob
+from paramiko.hostkeys import HostKeys
+from paramiko.config import SSHConfig, SSHConfigDict
+from paramiko.proxy import ProxyCommand
+
+from paramiko.common import (
+    AUTH_SUCCESSFUL,
+    AUTH_PARTIALLY_SUCCESSFUL,
+    AUTH_FAILED,
+    OPEN_SUCCEEDED,
+    OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+    OPEN_FAILED_CONNECT_FAILED,
+    OPEN_FAILED_UNKNOWN_CHANNEL_TYPE,
+    OPEN_FAILED_RESOURCE_SHORTAGE,
+)
+
+from paramiko.sftp import (
+    SFTP_OK,
+    SFTP_EOF,
+    SFTP_NO_SUCH_FILE,
+    SFTP_PERMISSION_DENIED,
+    SFTP_FAILURE,
+    SFTP_BAD_MESSAGE,
+    SFTP_NO_CONNECTION,
+    SFTP_CONNECTION_LOST,
+    SFTP_OP_UNSUPPORTED,
+)
+
+from paramiko.common import io_sleep
+
+
+__author__ = "Jeff Forcier <jeff@bitprophet.org>"
+__license__ = "GNU Lesser General Public License (LGPL)"
+
+__all__ = [
+    "Agent",
+    "AgentKey",
+    "AuthenticationException",
+    "AutoAddPolicy",
+    "BadAuthenticationType",
+    "BadHostKeyException",
+    "BufferedFile",
+    "Channel",
+    "ChannelException",
+    "ConfigParseError",
+    "CouldNotCanonicalize",
+    "DSSKey",
+    "ECDSAKey",
+    "Ed25519Key",
+    "HostKeys",
+    "Message",
+    "MissingHostKeyPolicy",
+    "PKey",
+    "PasswordRequiredException",
+    "ProxyCommand",
+    "ProxyCommandFailure",
+    "RSAKey",
+    "RejectPolicy",
+    "SFTP",
+    "SFTPAttributes",
+    "SFTPClient",
+    "SFTPError",
+    "SFTPFile",
+    "SFTPHandle",
+    "SFTPServer",
+    "SFTPServerInterface",
+    "SSHClient",
+    "SSHConfig",
+    "SSHConfigDict",
+    "SSHException",
+    "SecurityOptions",
+    "ServerInterface",
+    "SubsystemHandler",
+    "Transport",
+    "WarningPolicy",
+    "io_sleep",
+    "util",
+]
diff --git a/paramiko/__pycache__/__init__.cpython-311.pyc b/paramiko/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..23ab9003f981a48630ccaa9c6ae7f47ab34ba21d
GIT binary patch
literal 4197
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
Ybw?k6jT{W!h`?>S@O2db1c!<6A6P-e>Hq)$

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/_version.cpython-311.pyc b/paramiko/__pycache__/_version.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b46a253dae2474e01ab408cb680575a8a643076f
GIT binary patch
literal 362
zcmZ3^%ge<81RE^PQv!hWV-N=h*r1Hh9ze!)h7^V<h7`sHj4)|rnu#HmDU~^uVHpzx
z!)hRgfMr0ntKsUSSW=jR88n$SnO`yk<zIp%UV;?<V$}1~WW6O2A77SQRGgWg7oVAz
zmLDH~izO>RGw&92ZeqbL=Hil~Tigg0@hcfVgZ0MhhZX~M>1X8^rKjd5CTAq3<m<cS
zCzs}?=9Ludd!!a*Ch7;L=B5G-NL4V?FDS~-N-c>`0;){S$uH6`0ZQoSWF{5s7bF%X
z=4NK+>%*L(S5Wzj!zMRBr8Fniu80q49mvVWx<KLsGb1D84PluJ3<5X!1p70(GA0yH
fD4t<_LBZgnxZxFX!;5@ISNMz?xIwUp2dD!8C0bxQ

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/agent.cpython-311.pyc b/paramiko/__pycache__/agent.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..37891438a455492bb856136e200af361036ba2aa
GIT binary patch
literal 22508
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
BYpnnP

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/auth_handler.cpython-311.pyc b/paramiko/__pycache__/auth_handler.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..af91acee2405b7c154019e9255c17551bf74819e
GIT binary patch
literal 47384
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
H5t{!OUP$a<

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/ber.cpython-311.pyc b/paramiko/__pycache__/ber.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..76771d85eaac81f40f2a66318a318d8ab40edeec
GIT binary patch
literal 6716
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
Pi{Uf9{reqybqW6u^uQe`

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/buffered_pipe.cpython-311.pyc b/paramiko/__pycache__/buffered_pipe.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..12f24856d4cfc6d5cf8c828190c1fe28ca9c16eb
GIT binary patch
literal 9686
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/channel.cpython-311.pyc b/paramiko/__pycache__/channel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a1884c849942a3fadd8f8b5e61cf10840679428e
GIT binary patch
literal 66597
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/client.cpython-311.pyc b/paramiko/__pycache__/client.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..98281b550163336402c5d1cc95c95247a43de8d3
GIT binary patch
literal 34752
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
HF;4zJ#+P^b

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/common.cpython-311.pyc b/paramiko/__pycache__/common.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bed45dac8a0e8745654f83f0694846966f4f5db2
GIT binary patch
literal 7992
zcmb7IT}&I<l0FS>Z15jo%)gBxK==uP5QdORLSjq<9>6BpB&26DzQ(6R9E{DhO@6#t
zjc1hyBIUtXv(n{CccppQ`==|d_GxuTnun~kTJ6)Sz4A&{OCx2q+6Ueo&A#k?nXPl$
z-Ju)yj<&%?)mL@uoT_uGPMhD_?M(=tQapoy?nUT7iJ(3$HRgwZZ9wQ32qPY02G^w+
zo}qv1_&Qww3i0)r1<GO*P*WWe8gK(P|E3P1Kfp(MJ~Jc4n|TZ0$T#s;-p1Rhodq`<
z>|g}%;GI<4gslcG3)*JBg>U6u*v7X}D;u^OtcVXc-~MN`9egL%I<V6)mdwz_ck?}b
zFK*^N)T#xy8m#KU%FFlhKB{-&HiO;@`hI?ZzrYXjL;Ns*ksskl`7wSR?CS;dZy3l8
ze3Xa3L{W<YouFu=0liGoCIdQ2QL6!+qNvS)UZJSnfKF4?VL-1^)M-FxDB5g5KcZ-h
z0iC62s{y@6QI`RoqiCA}y-rcL0sWYw?FRG)MLP`WO^S9J&|4JkGNAJm?KYsdDcWN|
z?@+YYfch!wHJ}R=?K7YOiuw%bB1QWRC`Zu&1G+@f3kGzVqJsu>g`z_SG)U251NsR?
zFB;HQijEl2HHwZJ&~=KA8PE_##|>yWi3Brtf6WLM+zzx6cK~g|oj|R)3#biu19jjY
zpmy8~)QLS`GuVs!fckJh&;fh_=pY^fI*cy@9l@hO$M87VH{(lRGcZpJo&c#8Uk2*J
zlR(?>6wr2j1*jKK1MS0Cf%@<a(0=?8&;dLP^a8#HbP&%09m3av4&#r3Uc@(mj^LX>
zNAaz%nO`%)7@il#@ok|4-w`^oU+BUMLN^WwJ$O;*#az<zYlh!|Y&75{eKtNL-Q_O7
z$w#P`!OMp3Vgh}X-{SA__xWwSf`j-Iyo%R;#qtmEIvvgPpYorPOk+mxn}0~r1_Syz
zMMDNOM$xbV{TW3!3}~F9cMa%{5Epjx(B}9A-rzC5%L{mue}p6aE{^h#@fM%N_xLCH
zKEH>z`4oP@?_-`%3(S*@)*Uk-9ca=4NMC5u7a)n6B!ZOHq%8iF&*9JbL;R3`3ZG~A
zbN)HZ{sPxU&}b|_(iF(-?`P7!L?)fh#nZX0FP_G}-Be~Lp7K47i^=#-O2}SrIw3AY
z39Vm)1h)i&T|z(nGmu}P+$o(g<BU%G66Lfz5urWG>vLwbKcj`+S0C^OH?#WaPf51!
z6ZBV1u2Iz@^h6tp>s}(gHDshdBwKb~<b+t_u_!YOqYbj@Kuo4{vTHrE9E)(9_ksZ~
zw#nVy;v!MGDVa^uv?T<&o>(<xbK+qlC$k6f#NO{2nMq_xR(yZ@baE>zh}p>}8F5$G
zk0&0-ab|KclQ`TLAm@`S!a*`V84>mca3J_*ClAET6CoGd0ZSp35hrt?m`o*ivXcjK
zF}|PN%S<Nd#$7&mA(D-t1m0ax_CAmT`krk(8Y{8xGV7LDH<9}P-R}FhCSQr|FSGp;
z+fT8tR$i?XR=&5oi}Am4m8_n!)g!UB-|ww0Z*CN`$NrMlTef;7*82mvMu}5<he$T*
z8Tc76`ZJ*4Wqqb=`=ady><jTiAI!W&FPS{#O+N;9r!FLtA)~Blfj=VIR*}59iSVic
zTG0-FWOhHE-OG|i`h22;AdPfK2`sjPJPe=gWgw8HKOguv>@QbJ)}gX>NMeUXCk#2i
z079Voe+2K$oeCTdA%yzjMd&ZlO9Y`Zq0#yeQOflh5sNmmaC)AUxRjGhG;Yw06YU_7
z0{XAhVO0@ha(!=e7Ma&eZ<dajiq%$Fp~0LGUneA$CvAkZ6EaQ6EFm`unJ45nApt_d
zglrHJA%rJH03tWxWEOfzT7c8vklamYL_xM6rjz>zDLB>XoPcE$7IqGI^G+y)r^$pc
zAqrm{!m-XfR3M&62nRXYyeB+Qrjxn6MR=Z@NTwfUWOG`0wkN#Eo{$qRo50}e)JdPl
zUpet<{Pan`#$P=d(D<2?5tW~rIvLgYEBWR_SU~(R_gGue2jo#anK~4OQ#RQ{SY<YQ
z3Ypl6L)>D%PxCW7HFB^wk=dIhi&wpl=G!%c8lF9Xt}EmnO1o7J^UhjkCe*n!KwXXv
z8VJmpn#ddo>3rL1NgJd`)sb%1_E|EGGtVaC_{kycf#wj$bMd@p1REfvv4AHiY*oh!
znMZlMDkM@Fh{36f>aJ>l7KCb{E~QgT$8I(oKS)CSwS>5hnp|cN;_NrV!gJztSD1ia
z2<I#(<j;&0L@^`gd(Mm`@Py*lWmN7b6DOn{oscrk_xSNX97kx(@m%t$kb2>R=KMIh
zlY}#!cLbC(?;}Bh+_sh8OJ|<JeX4AbFZbd=$hT|?+03Ds5PaFk88H{%6`+ODY9nn*
zyiVp~fsEFlz*j_cLi*SVX<Z_%!5;}n3z0Ot6j|DjU;g-Iy~2Wo>QM3@pqP_?PsraA
zQXu3<Lf!&7nFFKUzi9ir<^RaPx}Aj9h?3AJfHcFbdD$F`;Y=bHlg+8j?rt)@tF%?J
zCfYJ-gON=lNw(}DSK(qX5(tMvTmWuCA_td4;Z06%A^cV-xW2K*t#hF$w<tGKb8ca4
zStP;9twdIDQhy-8ZA4`o!MN>cEErk}%T^+-a@)aBFscY4?*1zGAR@OB%@z#yZ$($M
zYgmsi`Gad)uoOKS*@BrO5#=7&JS_MjgiU2h8o03<3$Myux~a&zwE4QvQ$;qyp$I4U
z>U7JIh<_sp18Z8n)0#FKUgbja(0K#7?ZApZw9Lf<VUp1(XINIv1Gl*u-jwyr`k(^;
za>sez*MkAMXC=720`D>(vbqjw^23zDMI|rGYvBd|nif2L5vsia#dKL!L3IO~$(&^U
zA#N=e-hdc8jfm#ptVZ=hQ>J>k9}F#q@5lU$pKQVFmu`YZf7GvJM>U4?#Dx~Q#TZpN
zPtVPTmz1Nb+6LCbuz55#6;faFbf7L^)lyxmGePD!RT)+a)Jmh33i{e;Qbv&CklQG)
z7KS#v4y~1ooq}4u*r=!$j3TKO<EA?8Y^!b!uo{5@R-;vxra7rqjZ5!ZDVuIRsH>Zv
zb9J@S@aUDs$}#xWmCo?FL(kUE`9755F|18rJ)P%HK*~qSfuTf{bgAW_Poj#d8E>Jk
z)ssZ4r0Wc%mB~3O)sYgM>PTNG`blUx=*{(|(yUtmsTJkys?L>SQKD2a^<e2yee9~O
zx@3AYg#6HOWG8LtHS?vdHMxrd8~)8`(7(3!K+OPAS*eORf!oDFQl@A*sCD@wl@%rQ
ziFLR`BGFBMG<c5#FB_ZTmEb~<Tua@j4Qp3ftmf2rTDKKi4TbNAv`!X%u))cm(-zR)
z!&{s1y)d#8-i$);l-p|#gW*ukCv@;dPWF9h4%!tM3P)r9dk~F(VU3gfPg&471O8Be
zTZ2$z3lC_MmPb#Sgu=0PxOZdJbe)T?gcl>Hyfx+pF%u(Se`HH0Eht>$BX}37f&202
z3TIB#AbTpA79I%#R&X{UrXI=mnl|<{o;nm{m(HdJHa>q;d*sIFb5qJAvzgi1SxTn}
zotYtYityL2T_Y6udZ4mtk#<3rz5$r;`!_?u(6Ve&s?!qK3`T<i|C($`W@6ctARNf9
zMQ+KzwHA%l&Uz%sb8?3YuKTxR8~(s57mZPc6HbE8k^*`Q7;?(j?-BiyM89hN$#Qc)
zgAY@}y!b7=!@wIA%Ki`h*P&X^Fids0j$x~akOss!3l8#ALG}XusX9zdUlnPjxsDko
z#x>H!GWIIch!>ff3fxiu(cQ0BU#-4xu`qQNwza}KDy+N0+AC~Fg>A2}&F?$|$6Mb_
ze<S|#;jbQ+P<Nr{Xs>Eh1`izVe6{{+{k_#h26w8z9q-&75^Ak{eEW|>Z|mMJd^_>o
zL<wD!m~N?ewsO<|NAB&w+nsOMzgsV%>k{LU`sU8}I9g#n71mo}d)|3{66&e=m)^SH
zivKeGkDh<>l+e7yxFq+8G<K`ntu83>Rp?ddeV2_asI9{G(qC`_JWv-64k9DJkV98b
zapZWu)H+sf9V<91j`pL+#eB&zP<9LytQEJXxGi}nOYW(%dx`{PcOPvRlO?;aZ1)wK
zDjj{r=aTPgsbi+xF(aY2O1roCSn9h{YM(B*PfN&EY3?jqj(bbZ!{z4Tg1zGGIC_3;
zEjb6v&cTAsD0cdeEmHqSrOw%M=d6U>m7fNFdIg@L<85j9cFA+6?71VM?uvK#__1`+
zU-B-Ly$d9#zL9S%(kNH*EtP#s67p2e3>m%hJJ{RN_w{vSb^nckNN5<62cCYyb<c(4
zIce}lspn?7=ca@@&nD<h0B3-<-r|bnnJBegF1KBlP)ntE@OVWUx>f3(FZa$%sOv0n
zCV(?O&RD=fm^%)aT$jqOOA=~6yY*+*^^-ugVcbqis<>9I2ksr~C|ar};2?FT_TraS
zGvO`B<T!d(Z6tgXGIdH_V^u2=pu{?)&XKB}2o7XwE_PL&gl|Tsmf~2oh48Hk?jn2}
zGMSIeRX5?=6~2S;oeJMYcqkKM(?j@Ph4&C1ngPN42=7z)e!>qRQ<v0zsd|A3h>*b=
zGDL*onsAW_BXsrS)lnjh(bZqCjuYV$GWAKmx#|QFF4y9kB*IiJ;wwa$t_fF(FoR6)
zV!HYf;b)P_BY9`4*N8Az6Rs2CV`TD5eY4dYM7XI8xkdQ-n&LJQ?l8#IR(x3XGelYd
ziKIV3rA3fv(m5*97?)}o^a$9F3hR>wZbJKjiJ%Ik@sP@UCEqRRO_FE2x}}XSG*&-n
z(2Y9`^gia>k?%&J^A(&@%h>N$$rhdNx%P%y&mE^tZm`1k!x^v)Rm>d~v!`P2t~Q#w
zA@4vW)J`_C`F$fx#(?Qig`I$t1TAT$c=z?{o7F;N#p*hmEBasGcyps*so2_#n#I@i
zZ{|VM)Os{hoPK@j&831_X;G~|xBayZ8kE^l^pwor0$XWr1+l#oni!d+1DxC1ftZ^>
z_kWTGOQHUq#reiwFoCOAFTu8{<;^6_QUk56Wou8t0xoUM1<Sj3XzH#?cb|khAaGY_
T!3p(1|3GW*EjY;!y&C@)Dy@>;

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/compress.cpython-311.pyc b/paramiko/__pycache__/compress.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8d367f511b7483896e13821060afb16d95ffb237
GIT binary patch
literal 1743
zcmb_c&1(}u6rb7brZN49@%sy0ZA~di7erbF@nTJTD80m90y1o~TTR?#<IJ{T2s8%|
zMG7A4v52&UUcC5!G!jr2Jc$Qyk$URM_ja;t5_1w~+26jIH}Ac9@BL;E#>bNc#)o_J
zmZA~z4JYAHyTWl6gk55hJh7;yI5bZcvPi7hD`Khp3L%H^%8^$BjRu<dfF@=sIWjX+
zn@v7+%T;a*QFJSMv0QcRvR&~^52iw2ce!rrkGWY9RhN6S$sg!=rO@>sd}w!ad<BGE
zVv{_DvndvRMe+~@P_<$ZOZ6x6PH`;^=kh{mi4O40F?f&X0CtJjqbo)7KIjhNm7`8r
zl&M8>q)At1V!m2~ApU5fo0Yp(@@cI}eO1`bx<FU@$rEE$*jyMTmp`@3X5pD>xyFK9
z*ud0<v1C_^rjdg}2#?$PoKfX&$@bV9?6e)18y+YO2qTQD$<1={xod>+W~(p#1Y^ZY
z(PON3u|MP4P;nKGAua$k$oKU5cKSvuJ=soAZmT=-ZFr6`!9tWAOtASuc^V+>k~%5D
zSB|}?JmCs{>vSL14<lWlMX%?`$cslJDTQPs{a$LYB}X{!oYD9x`1px1b6;C`HpDYN
zf~#YBmR((5X4&P{+)`8FqqwMAre_KauIqd(FlMY^I?m8aNyT;8CT;^X$d}aFoy|8}
zueaWMt<+RIHMQrx-#n<ltAAW*-+k1Y&bFtstyK0Xk>wL$u+r496BV;;Gv+55E4$W)
zgYp<-FE&g^YWO(p<>vvKB(Muk#<2haAgjeZKqGoOilR+*NCXM0Ez%Adj?yQ(`^Q^z
z8acFJhh_f%_eQoeCL1?y{fV?z3R=wZpCoUNtPNJIc6snv5xJ*uyCnBlB==-1eXE_m
z6_PtBNFTY8*gbc@kE#)YVNWy$$sJNsk(5mS$$_5*qh87dogR94$+v0Te+i(=x6dj4
zQ%c`E*GgsDsmxI#6C8hqV}2aDA^VGL=GQ`)1VP*Z=;aL6GRToG#Hluo(?O8#&=dSH
zM;No*0%JjIBqf6w{c%qc_a(I-@S9+V6%%!UPK;98$tY9>t^y<o9czr@(}~9^GIlur
Y4Ay`3YR_mC4*ECz#I;{T6hRul0p{{*00000

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/config.cpython-311.pyc b/paramiko/__pycache__/config.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f37b507e006bfa43a51eb3fa0fef1d437fb47f0d
GIT binary patch
literal 25965
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
C4|}En

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/dsskey.cpython-311.pyc b/paramiko/__pycache__/dsskey.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..28a9f970ef39224f7fdacea0d65009b0ea4ba7ef
GIT binary patch
literal 12015
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
RK24u|L`$BmQ%*Vhe*j)v#vuRz

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/ecdsakey.cpython-311.pyc b/paramiko/__pycache__/ecdsakey.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4a81c78e6f75ec90ce185002d9224c3776ed165d
GIT binary patch
literal 16430
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
A0ssI2

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/ed25519key.cpython-311.pyc b/paramiko/__pycache__/ed25519key.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cb7085ce756c252b6e78240bf47c549a5261d1cd
GIT binary patch
literal 9155
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/file.cpython-311.pyc b/paramiko/__pycache__/file.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..adcc6db4b684fa6911f001c7af7b62a416d26265
GIT binary patch
literal 21478
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/hostkeys.cpython-311.pyc b/paramiko/__pycache__/hostkeys.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7703287d721feb59c18fcd6c57d3aef8daf873fe
GIT binary patch
literal 19771
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/kex_curve25519.cpython-311.pyc b/paramiko/__pycache__/kex_curve25519.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5915562984b567b9242ccb6e3d82502d5ea9d200
GIT binary patch
literal 8386
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/kex_ecdh_nist.cpython-311.pyc b/paramiko/__pycache__/kex_ecdh_nist.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..778bafef551b9dbfc35c166338a008a96498e26f
GIT binary patch
literal 9198
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/kex_gex.cpython-311.pyc b/paramiko/__pycache__/kex_gex.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2d11d42d3c38907e88e25f4b3eda654268d24029
GIT binary patch
literal 14430
zcmeHOZEO?Qnx64j#`f4w;v|p{h?@`+2a*urLJNUHOTt%4OQBrqwsK8oNSyd1jE5F-
z6K-zBl?;m1P@zkTSk=2iTvq&8c2#LB?T@{Gc7LvH@1R&CMT@k$Klo=v@1IKh@xEv5
z;~9TI=#{Rt(w;c4&z$p~&pGEg&w0=B?_Dkj1Mcx#dxa1B80J6mq3Emy&x<^G?l1xq
zU<6h$Mc4of-{y!ZY7UsAmVkxDa7)A*wFPX%Z;jMNxd0cn2kcQtz!7x@oFv>9;iImA
zi}>pz?x-i=VHp!6)(f0qe_&#mNARz?PoeZcL!dF>4KxLs1;_OodPkTCkl;}<8AH;w
z1U3XV23iAcCgvz3IIlATZ_GW=PGVdT;}+W=m>~!F*Ib}OFr8uip1G{!Tu2IsVzMtD
z^L>6&ymC~EU!EQIjm(CmP&7OfADj`d1Sd)0AdNowx$ny<Q4)Rfl<1q4!co!JHQVJ2
z#RSKs7!#$C9G84ulMn(rvr_zvupkP)3s;GLLX^G`r9q$Xu<t@x_C@2M^JQF`@r4sU
zNxXD9EP*c62J&`HM8YD}EdlAbWA?s~ufIPMm;3v*beft^%aW)KI-X$@$NeVNl9&n&
zt2U53f3e5Us+P-gIO4ac)+3|ud~{UhE?kwx;KY<v@WmxTwUe-DC@};2b*IEcA~Y$2
zmp^mn*yxoBaaIn;W6w520nfTYOt4s1!QoKC4t}v6#2sdmnP)z0SYTzH&OG}7o*vPV
zTc)MPGtA#Iepaf3L{Mkb;0~3YNZ=6h`F{Fz=p$%aVrV)pO^VUb#8gO#4;_h5T#iDo
zB!-TOv*FOt88Ip*Waz{_L(tXJq8z*cnqnj_4apEO6bWBQ3>9DJ5P6;AmBHDoKRHGs
z;e<R9kIo*Hyini{_)lQl7MZlAPx~yN{_Xi>=r^BS|AYp;01ajWMbQ-p^osz5++jop
zCJz%Z32eYDm;x5T9Iy(OfK99maDw$Z6R->QLfv&X;1D=?atd~kJP!YW3kPz*E$|>c
z0u0?iz2FAfAb4Q7HmEing5s6AE+KsJVptr2W=5i+*Z>)z1L76vq1dE2fG_?TV}ZBC
z_!uV4a8Ti5C9%@zC$H)Y@Z4eM*?A_2;lv5>;)3)Qktdp#JYmX`CX}LkD&h$s3FKm<
zaaaPrV&Vh_8e<wC^K+_QmO`<_Y+RC6c2;FCsqCc6UQt<5WiP7scti*$<f{>pjAk)%
zQEGz>3Zp}61i#7!gW*_M4hH8uTHg#71NNipK@f{fuC*&^S#swZn&Hc~p0C!;m>>y%
zQWIn`X6Qa_GU>iC=osieW*P!CH%&WE>eYv*XcrbW@mr=H1b%o)MP~#ufKuo%*;$Nb
z%HVLpG|x&S^QM$8x00^xF8PhKk7Xc@uGEqq)ZAWtDo-Qet0W5wYQ&~$mfKRy0qP6A
zQkwNIEtfB*dW|$S>-v)RxoVc9dz&d;-xc*4=(UQv>C;qy(}*htSDv~s9nSje$?#U4
z8k`5CaY0nwLGjA0I3WjTLlZNithNMCojDpjIePxcvEb3s^T7{CPk%Ie=B(OQJv{jS
zdq*UETPhb4gdoDZ+CEWRSFJ;}a7<R+vyymGlwdIm!ZMYR-oQ*Y)JUpw(Qu6DfiHAL
z^SOcvF(w27opBXWo61T(`r-Q@ec(4sU6`>ah*%w@6JBMb37ld+pHG|T_Jkag<RHNJ
zTzz4p(~wb6O#BxJnCVW>(m}=9lV>b;Z;r2D8eir8S>C@^zx7GqJI~D~m-|NsM4kv6
z<MJlQ{=_k^rW?nXjwjiivn|iq?LAL<*ViX*o_MlhXNKRU@VnCdt{em%zj=IFP+I&M
zzF*<{(|mt!(;LYnOXJCLnqV-)4=MamnjfMGZbvix7KPuE=C{x=Lng(X^@<$7<X+{r
zWVtP=(;2Q;;d;|tZ%zyC$#OkUw(iSt`xS0~y7(bLH9^4{x&!fm;MDJ6{vpiE5Kwe8
zWSmOP^Gu3jRxu90%SZVcOi#g{VRY&BWzsCFrq`j3E<If|XyRJsQ*<ztPZyR~rmrt)
z=*p+~QHrhBet}&u%grU7d2>pK$+~c6n${B-5A^dRiV4QK{}l_%;9S2po_)Vs@B#4x
z@+ZfAu#O86;ErU{`{b*$qVHGxgayA#>V<;TjQ|HyLd5AHOa&)_q9Sud8Z?a2Efvv(
zAgJE5DR_MR_*u2NLKzp-is|~{=m+n8TqS2ooQ+(S`morJiku3y!g(!WU#Yf>aVZ*-
zRZA=s75x@%@>^%IfzmEiFh>)U2^?lH^9xhop|v$8UXkY-3Uj{{^eN~j{sjb1{`x0P
z{aI&!+S#A;Zdmp1$a;4?Zpe6dDPA&#U5!s#`m?V7v`dR0%z6hOUdnif74LAGAI@RC
zKkM?RU4D{&AnP4?7|M8eDBc}ueg}zvRr(!S*N(Jn2T8v>>)rkMbjG_^@$OCYdvor6
zN%NY!ZDrSY`|j>b3HQT~n=|cumG-?E_nV6Q%{2F>Hi`8U68nNo;%nL@)-5#xI|tA%
znC4)30nye?!)ZF;=3y6S9D)lbSvO(kO@g@<iqM>Kc*1bkO^xyiy<ib63)XqFV4Z`S
zB|P9`=U{>@nCGqFgXO;z`*$9@fxt{tsxogWE7K->inW1BXw%nj-fC<K!~)U&t*KQ1
zIY_!-GxP+79Mm?ZU#Od}6WAF7Y7+S8*%?x_#KzdA^YF|BY5U0{Z5w}vujU!{u7u**
z|Dad|;de;w@FgLZsk|UwjD%o!6^X|tC9HvJj)*bU6i=vimx1vL@u-AZt9&@7(?pWs
z=g6dxIxyOLDF*XJwOyJbE4M@2vc)gM`fR;~+cdQf7F$@<6B4FPU{N@6&}r+Y6VQR|
zNX6ilxyHim(S=1Y*&2vN=BcY;>D+SYMquS|hToKA*Xo;;`ZrQ<zyeg?tJL=<t!oXf
zD?KUpUSFo6TWRP{+SZykt~B2Y+zuoix%#H0HRo+zIdba&tgiNrIlgiE(vA0$V>!M7
zay^+G&0CqKHX5I|S?w)rEgNr7r`j?t+m)8>$)js6ovFHq?0v_>(M-!urDf;iU;c3V
zhYx@F@cU0PEr%dpt5Wa7;Y|I2Qa_M9oU3nGKKzY!*}8H##oirV8O_x<=beyqo@W}p
zNcWTQr%fw5DWJL7Q)F5l0*Py53IZzK0|<IVgOO>c=#$=4re|KKo@L$)T$@gJIuvzb
zFr83`*1a%9VQ`khK*uz1E~Ew@)Dv<pXyUU4;+Y`L19J>gxBy+<h7a;369QY8s=6|l
zwbeRrUG&J5PXi#8?ef2Z9fK}U9r%=bP}Z%?f+<C(T}cC1WT>~rXvi^6XxLd;Q*>|g
zs<fX%e?ZHfMLqD-bz8>CTySOWW#Qdc-51C&;rNh7*TQ1XSbm8s7=aMlQacT;b440`
zPdAjxDf!B6Fi?&@4Q{F{oV>LZR9+$hM)UYj1Mh}ny|Pb;7pzeOXteSHHSv8JmZyBN
zI9RNN%aO|oLTctZKL}6EgkzJwSzjLzpOFJde>92P4Zpuht(yc}4s1oFAt+rMM6n$O
z+5prBz%UuNNSGz?j)@tKU|0ZiCnX!|9Y8&&PEFc`!33spkVt-1XfEH<kbQ}2$9+(A
z7K`73y4VBKn;`r)4W9{Hse^3~R#$b=-4c1MgG~_eSg>XZtPa6s4V0xl@I2R91mhZ1
z?iwg1eht_F#HP`E$N!E0*0$T*l6AR_t>4-2+P`()b0%Gen5WK-E4%L<$U6Jd&c0lS
z@7}58sa%z?Jjd|f<<GLtuC%i&*V3IlnsYW9o}X^)1n6|-Hg8K>6s|p|JwEk#m#(dP
zhO(ZajAvN!3@0tH-%5_GHEvFKj%OO*R~p|>^Y0gSDeYOVeT{F(@dK;;?kvAMSHEFp
z)5E&b9_+c*YIpp|fXEZUGi{q!Tm9Kq|HIzL2ma8TX+5E|p19#g5MQ?3wyp8r!Y+-o
zf9<&Gc;fBLaDIjJr#XKPf}A&<%SUdHWw>^zavEw~HSCr9s<$%SK84$tE`BQ44t$+t
z?Qp!t+EIqF6!T&BQCuz{IIj<TNji$)1*FGxG^{M$>tVWS-t-s3blpm4M3xF<H`PFP
zLz_!$slE@5YsUZMK8#=H&&GY^5NI1Ej;sQfap?>^%{A+={TkvrLG-i!X6Y<MYam|2
zbcA#1BNVu*N#{^d{FZ)+k5zD6I*$<_qcGw#F;o@IN@PnZeFjPfW0EuiA#>fYz+c?7
z-p8aT2?pnFOsDU=@ZIpYGxuf)Dn5036zAqt)BR0Z=V017nCsele;|28;X45pYsCC4
z2u*wYSG~ho@9^U-%FfY@cTDk)0gNJef<UtEfZ7$o6RlpSR(31xgO4rWyFjlE_1YXI
zApH~Ay^02}zYvt>S~mfVmkjC#dpgC_e+495FBdo98=Ha`Rp}l;&?6cQDJatFx4<QC
z3YbB0*@#BKsNp<E!`Xxl{H6j{cj@i&g1Nd4zI^L!%$x9I%8IL7<YgT%6D;d21zKCO
zJmGw=j-bX~Vi|b@?K0$sw9mj#`LUs=V9hBVtwJx;y?~4px8RJWEFg>4ZS;#;a1P=t
z`HlTV7;$w@%S_WUE7CC2rM}hmVyTp>jJTA>Thc7~<i3)>yhMV9)^Q2DQQ8P%ZsZI(
z^BpK|#RbBPf%yt?<ji>7il@Kv%Ww>R(FgW{Ns+K(f}3+KN8@k=OmZl&WfJahR_%n`
z5|T<-Ex}1*fpCCI5KIn}@{|Uv2e2{(YD)ln8p2fbZ2U`NJdiq3r9_OI%84;r3(FCA
zAEeQPt!jgUp<E;0?gg#6O||fr*e3rOR1)nP+ihsP)Ba8StxdNPGvK(+@c74u=G&bB
zx-FT8KBb{AX+t~1*_%LhSH5^?xx28k0CY5OVjDXCqoeEYSZdF=<M+m&Tg_fD{Da6d
zX0ZNqjNOIyb>ygjHJa|;zuNsyw)>qw?8$VWP`XcKxRVNZGR>U?D!a;U%yJtuT${qR
zrMb30HZ(1ty?t)w?7efTv-i(s8n!D9+p(IJ?6fz-^(kCmn(O<SOtiX%pUy|<F&w4H
z_+A_Y4E<TisY5)9Z(jZE)+2ysa4aC7M~|UG*_jw(T8eT`s3CPm900VzRlne%ENF-3
zz)$C(z79q%2WW$q9OBAyqDa2JoB-i<r=IIU%IemCWBb5_TCz@-pW9mIEh&my#a7_A
zz9!Su)?f&w*l7r*HU>i|#mss013-sI)T%=`AjQN)8`P&N+CgrV>6N41OVZG@l8Q9U
z^!iE#BeLcya<HUyt5i{o^c<ZZuaSn1y4OfU$JcA5p<{L3Y0BmmS;cWpsc`TfbTg?G
zZ5)rra#OmoSt|t?@%8fpmyikvmbo(1JQ-Qkinp%Wq_en{3Ya&PK8;3k;*7gPF!}3R
zVr(&C8af_Q%mJ7NZUYL=*e`@>;Br{uU<Sc^H{4}lYmUU)Z<|e3&)rs$yT0#z71e=&
zs_doI^KVp3W9%XAeu>{aF1-uNbDQ7QE}hMQ>8?xcDkmoXF6nbn)7S!G9XRYH<Py>a
z6oiDJDOB>IzyYJWk*E}|HOz=t;g&%{JGDT%p_;+grn)6D8iykjxEq&%OJ=Go5}yb~
z=%c3)4YeW{epI&>UwkIpK4}}4Sh5t7O(yOURr^#ZF%=9&CgZA22v5REDcl@NK&4Iw
zkEyOicrq4DfPxT`L#k^k4p$MNnn7|bS=~yg9fvy5N(iT}V8|rfP_;1_nvla^;Motb
zxeIVoBlzod21E9wYQ_#QO+{7qm}-WS6S&N2*1>{#@GS_K>!?MS@O1XyK_hV;kPd8v
zMtX$1zp>@hH%}x_tQ@&Fh8*Q#=&u9Wj-hnN5HZxfooPIzG#*Oxhbr04T5WdYK@;4H
z<(@5Nxo^wxy$at8_a)qoOR-h=wyb+w#yz082Ugv`$hv=#aqm^!dy{6^Xu6wke7y8=
z(v;&IU)yd1S%WJX3hys`_Z7bTOW~g9P8;rQLF9>WGxbeN*YXT&-<spycr9aDzP*s)
zdlbGW&G%r&U<_Q|t$4SVE=r)K@})`mn>_Nk4A-G>9ciuuv-xwwPT%k>d6FJz%h%&K
z$5(dZDet!<_eL^&pThU8@<UmED8mmc{4n&Kc9o+u%XOyM)NqFDQMjHo*YmWpNB3vl
z`{Ck9QeL{Ic(%cY-h&%@j|2KwcXOrsMjo4VCd0KVTx*(Zt<FR3q~Ut4m)J=CS<au~
zwkh1Ubn#PZAf>eQ8c@LX(o%A`Mi*5W;M5u6O23VVO(fOQdyFaIcucpAHPBp27C?iV
zPRnR61yek0Hv;rmY#Hg)=VvR{NT0WnzAjkwr`=_>hJ&^0#sK|-JWY?g455??7((^i
zciO7b))~wcmGqF(PoRg^>RWawgAz|;|G+|8I}N3!6=|3f>3wObltfnKV691H>05q{
zG_}cZ?Q&|9-`Z*D=vjB&%5G1T(Jo4#iz5-g@wm4JWwEBJQcL5wG}diB6w*l6%Ia8)
zP?p_VDN9wAEE?-sPHQw9jE~Toi<PvdLxsEhKvJ~D{B!6ijlYyA&IJr3#D*A2rtlGK
zFHNHeqlloGL4mxhCLPf@60x_$Q7?*O5=9Jz>O|HPzIYWmh$KUZ;mnaM(i7<_rnrpa
zOAr;OjQb(5w$+8)+xl-z_iq5Yz$3g(cSgP$xpnaN!6a8sD%_s0BR3;AVoR~48JFty
zGK@98Ds8CA3yOT8$PbF&zz>@K@ACq~lE3@+2mj{a<F0?~|A+p+-~RpfO!wPL_uH%8
zW7+PpO!qOR`&foMu5icG-0=cc=*n_ksW%_OIz6m#!)b1~5MFCV9{00j{zkkYk9X~Y
zLDdn|&dbry2ZNU`hay^xbP&f7q0NLblT&uYNo`<uqi;8g!zc*T--ABfKS^lYlaR4W
zINl_*OAtapt{+K=Kt${UPvW0IEYiQH&Y{I4IgfYom?oNA7sufi(c<yEg|l|%87i8Z
zts{BHC<b}!TX}{GU%l0vXQ<e+#kxPwQ1KqyYTcY?sMynp8B)>bw{FifRGecs;d7<X
z+J#M;fPd<WoaF_^7=QjDTHvQ2_)8<R%4>Z7%(27pYn-_)ulxZAXpO_XzXo|qzecf$
zg1jjU`Uqx}_4Zd7fnqb%DS=pC>g}9!7<#+DW$`%vX-z>@|0cCA7!=|Y!Ju>;p4B?s
zD#4FtBr*c9ER};R3gp*N+VwV1@o^A-JQIz_wBtG?edMm2cC)RXDitn(YuC_QXfW=#
z;1?^}Z;G_b8&+caCYMRcMOJd*LfVHJ_oC=WaUKP}07*hIfPxG&(t2Z${KSorD<a}S
z=_csmn@Ws=%$r%3&2P1^7Pu%5f@fIAq8<Noj4xg7^OR{#8$UVbQ2JG$9J3{D{N$LS
zwDFT~WUP+G(PUTh!f#GpKlR*YV@)~B=A30?&a#2XHdOJ$50F-RR(evV)H~k}+#7($
zjHOSp^c50x<}9s58)SQ)n=-R7hO5ixB>YEx?72D<y8%uTE5!1i=NJkbj*2KUhW-~c
CSlw6v

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/kex_group1.cpython-311.pyc b/paramiko/__pycache__/kex_group1.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b6016acac1e90da55cf6df50cbdafcbd69608890
GIT binary patch
literal 7998
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/kex_group14.cpython-311.pyc b/paramiko/__pycache__/kex_group14.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1a3736a7276365057bd50313e2d8b5295f3451fc
GIT binary patch
literal 1402
zcmZ`(TWl0n7(O#Qd+mj7*<MVQ9v0nnNxRc+meSUWDHeChD$y=&(h-=>?m6AtT$Y(#
zx?NEA!5G0r2niB>At7Eu8WN34VuFTviHXq!B{7?c4<^P3d||QpLP+qO*)0XB=gfTb
zpYzY0^Z(!X&-tjW%>zjLczh=cIsy2D4%V8zX{;V1#yNn101$&%nNe7QWvFeFZ3-uF
zie0cXfCW*2xYq%)WA;NE04wBfG{Ir{9K`3uj{o?a$dv#=cjc5PQIu39=?EqgF__28
z5SLRKNljxo;Lqc-A7&TfvQ~milB!D3&`K(TDNU1uAqc0li;G!26vMKtNGjA8JtM=B
zF__F2t(7v8d;}YVuw-P_G?ZXo)s|G~FZf|W&ng&_jiEp7hiR;0T`FpN$a90n9%M{+
z9G9cIRw{&ptjQS}DQr3jj_epsHPf>mOmnchg&5}m1_DC}V-X|R5G!!VCfKn<a3YQ*
zYd1aj7mdg#$Ore1H~Oi-+1U%-(eW;?r<h6(cN~2Az|aR1Up;wtd^of)`<Zwp`SiRm
zv9o+|Prm=;mW7^)GBT=LovnjQlhK=Fmk%dV%S@~2ZT-Pj%Y3)_Dq2Ao9pB84#@{Kl
z&+I??Ti2}X+uW7j_V}f3Uqp*teaHTJ;r;%<HoX;jFR?OKgTFjKUsxI(2<-x2KXW<u
zX>{bdGw5!rQ*?En55=Di<V^96)R(VbVk)m3INme&xfp&iG=1XGC;fc*$c~o1J?^)2
zzs?1YU2i+d|E>7uuFAbL*C$7}{drVnBkil+fjyn`<Kgf3-(;DIAJe{$F?Y=U(^OyK
zLT@S=+5PkGH*>GW?lyV22X3&0mP#LL=$dKhI+V7AhzwB~_61$0OB7W}!J=q-L{ZUD
zNv3wIC>}0Ja?``SWHM2cqa&Roi<<6?WMo81PHPvzDwzOx7Wi2M>jt0G^fXo^Qguk<
zr?pf`!D`XqW4Mr&_ykt4QPeRU<qNu&!$mPk-o&z|^F`v|<!sX63z9A=*}TS+f)$(5
zh6>9%`L}=tj}yaqia_;#uRDM{?y64JT<+@B?=1t>>6+J9-AlXPvG(1@#nw&S4;LHe
z1Uq6$k=ab|dXXh!Q>0)kPy9cf#Jw-*(A{)DdL-yA6xu1!b3orj!Arpksgqj1H3$&H
z=q0e8RIOz@&7=FlJm~vWHK!;dEk%Udyhc}Ooi3do8>W+LL>RRcYeZmCXl?m$7<XKg
z4dr*~Gz9u18t)RQ+Zcwa2W$*i2NXPj*?tWKAG~S+UfbxZJDf~c9XtxBAG<|oZ?C(2
UEYnj5kHYE7EjoL91DD0%zrc!vhX4Qo

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/kex_group16.cpython-311.pyc b/paramiko/__pycache__/kex_group16.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..39827c0e8b915b318ea1461101aa461b804fc888
GIT binary patch
literal 1397
zcmZuxeQZ-z6u+;1?fSt+S2k8Qghv@;-RQc)R+x;!zUW|VanzAnJ6>P=?%KD$kMdsM
zz8E+Z2oe?0ghezFbpb(03>w@&Y?w?Y|0o8QjG|@)V<a(H0}(O|dSAPtM(@YDzjM#K
z=XcJ{x$kslrU9V#N!409sRQ7SqRchTEV%DRU<lB_2WX5|u^6ZFsW1hrSv80Ia15w`
z2hjL_Kx?4tlp27K(M&g=hSqw4Eu;U4!5gP|ni6Tk>un^Wu#bSfp$Nr?AyI6N!d@%E
zbP|1nO!QDZPZ5$J^E43>1lCqY5cN!FCj;$`kYzcFCxlKzMR}Er2r}`MrwGM%LunNu
zOAH?-C?d)WJv?EJS&5j)aF9U6gf(m>!jOj|6&J)Z!(Aml8<x;F!CsFj$T25Uv{Hns
zbgmA~2A^86S>?VJfgu2a4?|a>qA{PER{3yRjZ#DyYJC|rj#AYmOix|9yp1);U;i_7
zI{rvHetS4Y=(4BjSTo}I^QCluZ_yRdEavw&&)?TvD~|3FEF-%^%eTp&E$~);vBRxr
zzzMb4Z(9F-L5$pK?9mRHe_8&x?|kvWWj&h@WgWz4=<Tk~yy?mo&xPLiEp6S8YhETl
zY#Q+VfM3-7%5M(W+Z(E80%p#b%Q7chZv>XN?99Bf<@UCs7vsBYBd^u<a(~3m;7h9V
zF;nfZ>eYoKbhdTlx+nHqb-^9Sz3M%MSvCGvS4ZHG{$9JRxxQY`9>`NSUNTxr-G#?&
zb=_-LUe0nY!W(XCACTh?DepX0-#;RKUA|)LW!vihktJ`HztbAsGa34kYBsw4?{X7?
zlhL-ta)Uwm+?k#8UAU!aDD<%FXvs%w9uV0TBbMvF1MO$6c8{1#7=8|oms>kN@cUNC
z?k%}exj%5WVyybh^%rtZtxa|uj=BvS0%gx8>7PJdhGo^g>NkX4t~<f6jKlLrO_r_M
z?cX-6+;p|{sx7nbQ`hl@x3h+y|J`5OW~!g8c4i&7d#aAaO9~#zZx?RtT54qSPnYg$
zui5_k3)4OA#eO%a-R(NI=E&-U=Bon}=22bY(vGIRMK^q7+KCO1?dtxE{wI0oD#x5B
z$`0u-IX60j-Cf2v^UPV3JI)t9@)j6K@m9{cO*bvW-agl6_>jjd7E8;D51TJk1wCg3
z4Hn<_O^$V)xzXJGO2M?%85*zH-+Dkdx%cS6TxDS3t_qc4i%pl%kt9!XkR%fZlH>$h
zW)(b-BzMUaJ8Qv}HGw^sz&s+3&L#8_N{Wya8y3!h`$|#I1RQM=6eUNOAci4Fq1Q+Y
zj(Q;^bC8cqjz$<`D2EqvP>PF?a5-Y4&;{dU5Pb<*L3G5C!ND>?$q}POiesXJ13hGN
zwz|t=eWDi8q{Lzbq!J_spY|zZ$zXjF2l^({V9k_HKUg!Z>`a*!4sKDpXqr3RV$&uv
zNRk#pD9Z(NTxEZAiOT;bk)b4r!bzo@rk=03NcqV|Xhv6^zAU$xi&W)hOAHc8HHKly
rA~l95fg%h5E4c=2{~S{w|JuJtQk#M0CBc8i(Z1i5-3R|;N`>+lIWV`)

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/kex_gss.cpython-311.pyc b/paramiko/__pycache__/kex_gss.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..960909a05a12802afd7b95a0714a31c1a38b9ca5
GIT binary patch
literal 32255
zcmeHwYj9iFdEf;IfEU0e2ofMgN|Xpu;sc^Uh<clmWJ?zHFeU0iT8UsM5JW&CBoLtP
z1ucpu9XaEqkgBaEEfdj>I)M|%r6N03cGK3olVmg9b~@8(X8{CSVX);^Go8#-`O#95
zGrQw<C;NTp;-32eK#{gLlkMaX{BZ7hpL5UoUgvxVe^gavVZhycW?S%=eunu6e2|>6
z;CV8xW0+e^knu4=T~Hs<`E>AI5z&uS_$qW5W{4O@j6UOt$!8j=^i__qK6b?HGmlt&
z77}laSVuS?NBpKp)rie!BmT-r^@!bP*D-n~R1;)_=J)gra~J+a_cFA=SL<{5oW43=
zJxOB;S~Y1PwQrfPA;`T^rj)(Rdr;h6sZfTL+UQ&ETj6W+tqfKb@)ea_ln-lK<!jb6
z`<bBa4JKHvZi&xDV(bu86LP&*0d>N^=zOb5TrI>oG`U(xj1yw&G%;&}`T@pWANUmv
zzh!VZEVxEu!LdllHOj|c2?s-hYyZH2XZO*AF0X4~0KWP{mt4K$L&JgS*^nzBxPqZG
z;b<u63P)W%{F$MiQ=2z$-FT|YV(IE~MPu<$Pmk+)J`@Z^<KaLAQb$5(1MzSy>Vm2u
z-X)BUj>h=7Sb`AZUkUNpgpjfXiwoST<NKa>Vd1WU&=4tMW7kF!vtu|O9~FAM-gqn)
z5xT;m_?fO4f7Ux3ABlLOO_-?*AJzzBP7mbVb;|D&0+$5WMd$_}!nTmY_|S#1Ff`Z|
zkGaAlqmj@EG@TAYi05O_zK|dU&W408*WmD&upaw95{O=c@c3{n2t|hD!{I0?4m{G3
zLb@}qK-49c5gQsB;|20OJ`@@y9l98fL|mssuJ|w?yBLKyXvJ`dlrt2IMho4hsY3in
zI2wSV6URyjg#r;F1_Ry`jKPcW21dhPK^XR*6@*jdeZbfXF!X_7FcgGj-Cf;VERS&V
z^t*L=gD@QEcAN6Xy}f&0+@EJh0^|PEm*Szkc_PHeL@$d0{-I$$ZzjHxfN(x92FCax
z7BUtON8r2iusB5E<pu^0z=RRI_-GZ>_Gk@)4g~7}48hc+4)Fob=qKv|++xl$lgt-u
zrgU*dnn~Sz@N}1k+|XaE9boP-cXWIuWV)lz>$^7ObwdKKahL1km%T5-yb`?UU`~Wa
z0x)%hG4I~k&=|g1!FwPy8V+~|;5`d*m<L<EF!Rra;{MZ+G8BpN-Z(^fBjMA6cQn8U
zM#AS~-t(bx9M!JTOCMWyM8ZORC^j;>i*JAm_rbq_y}Zn%3?1U<`mwKmArW}}r8i!p
zK~EsXXVi1P7K-8>2%G>4-R75J0JeyL{TR~;;4(AM=)HOw<eYV1J&-u|dfyv;^A)NX
zz7f(7!9R5pu6c49f^IP(2Cy{a(+73FilE+S2v+!vfI&?`gZd4HDuc!ec(W6b%g2Vy
zK@&dtEWt{jHORif__&}MU{%lp&=#};tVUexvm@s8)gUJH)ds5pI)Z@ve9mAEV6ECb
z*B2TG{Is8sjg59se2c6U4Zhkv21^K#&k!yY*WU1%GvScuKqwM{6}u2JLv%yRQ==T$
zCqOoUYDh_uUg-a?zbyVWZ~yD2SDl-j2hTiP|G3w6>5=cFP2WE8f3|Fo{*T!5qkk6q
z)w2Q5zf}D!zUR<CR{hJR+D)H->H3ZvqqAE_w)}bX+|mDC`}b`D<KOICz4C82ZCdw}
zi*2rdXsF(O+<D`$<8z0my4d8<WLwSdfB)R{aMR1WWmg;9+SYKMy|b_UUQnvPnt6ol
z<I$H9;JSIl0+{H4Y3(^n#?ylc!4r>t{_vK8$i*$a7q=h2a6WLUGZ5K$cIVC^%q=<e
zlT{l9&n;$BH^~5@FStqOoaANB(Nzm6XbC}B<$h^y0j-B8gOr6lAyy!cJelYd1G7Ni
z&tpOE%Dg$w2ciN}le`sHDD=cX6d#Z0P2xkI#gO5c5YOwz^SV%8cLs2UK?p_8@GVe+
za^d7zzdsxe$Nm0^I^`Vel7dfShAjXtGr7i=gyEVkSHFBZnyudeerv9w3BIawHMQ_%
zQhy~(36r?8=vY88*e5cAKY`A`2r@|sD0XTtLkH>P5j{u8liwG|H(GFzhK?rq=~#lF
zj-;BCfbpmKl{F-BiWIpx;-7+3uY2Ub2AEIX7Q#JZ1<JOZvr2VBYpFkuD68PqV>P9l
z)FrE=IOTV|Nq*9{AM#Yov9b?ZVwawhHIh&H4Jl}=-q)+kRX@R7)Fl;TD3=0d(7qSt
zQM~yiBL5<%rIYH}Qs!MERW7(V5@*Se<XG}|Nj`>o4`$X~I=jU8?XH-3k>ESz+6LkQ
zK8{=rWuXdu<f8F%7cHy*jxKM4^3I0h?uNY8e>M~i@qpI-<7EBjts?vANA@vaF&ev=
z*X`%qps{(~Q64*%xA{ZkqrgV_M*~CWL-Bm|;eq}BzTPhY&F~-WKRC#@Ln^)%0lvIE
z3#^jJ`P2;gHEHt}j0Bz`96igg#net@wg}b$KGP2zTRa5V$BfBFM#Ir~zRE9zqVOQ{
zM)_Jri3g4h4EFV&%sUie&mZX@JlOwYuO{&D(L=q1y?MJLW$*FhM~>go^Z3T{x)Bv7
zGYj|%{D1=|>Q$(Wh6f;*&<NNFcmtL(S3O`GGHgSdU7lr^r`Y8=YxT8VS!?S&V=y~@
zUe_?=yj`DkzFR-*e7`<jw;@}%A+h(`z8q)2*8hNWXE^r<-Za;f<$6+FPmZfi?E86*
z^ZMZQiJ8IMCz6Bjo=DfM&(^F*#Q3nG>9#G~;6+?H$2qPXymm05%dzG+Emtk~9c$BU
zYnE+Iv8@=yUFD#Vmmbt@$<%H6aPUW8Ox0~k*Y#!V`qJ#7EPE)$9>QeStJdp#r}w4V
z6<Kygid~Uoxofru?Ai>wHhC<~wrAP)6x&`1ZOyQ)bKI&qu9<wA9-ED3%SQ}=c{LF6
zHKkB6UWi2rzWxP_8{+G85Fk1kBlE6FU7W5ChB;3#G+zhUU0Iftk(t!siGecZ3RtcJ
z7c0u8*qs&%p2e0++RMo~73ETTAjdAgT+&{OFID4UR+cCEOSD%PN8YyJ6z!roR*J*#
z;`~9~RE3f@OjeYp1zMb`#=2k8x%D5<P<(VWzzZm>f$#_m-6i4_ki`)1q3a>?f@?H>
zX*ARWqGHI^A)*Vn3xiy-GbD333ep20kwO95?5Da4veYTph**kPk&7Uhs3^5!brW5H
z#(<?A7ujJD&d-ZtEXe9Xl;{c06RUo;CphJ{5wyu~fDz-7mC4rvUW6_ou0U1^Q5nAj
zBkI%<$U~hEUE+6QT)jFDTNRFuh4@`1BT=G`hxlhP(xJ{t<YFGDU*2>k#*YNzcMKv%
zGm;_XcVkq=h;UZGHHBR!LQdWywi;i;#4;6%lOtY*9KtUF0FJXUfU87|W;T0gxBSVG
zOy~2d&gXNEWe*(dGLCh#-D!s>>+qyFPp+nJdR4ZjbDpU*d+*iV?>Us|K9uS{MACXP
z4$o{L?bwiYY)Ek%a#c0gzLc$MN95t1eewR*gBj1kl;<Ex+m&&2&0a`5y0eb%6xS`6
zTOpQvIMaPN)qS{R+N#?7^=+A|wp3LciSNufI%jvM9bH*RSBmS(*>)u==4{JnHhrt-
z?Ve;Py<&ZK#rm|(leKwLtf!dS#Ni{z>+isGi!n1w@mfY-K@B<rF<gOOCP6(Xb6X(t
z0dl)bSx|`81&v?T%PCO!LnbU}44RbTQ~F6gpihbr!B0`<8T~6dUKiC}I~G?gbeV0{
zE>|Vv8Z?4%P#G8n{7jdW#4(`SrDeRu)zR(R;Tnhk3-0x<i^Cz1|1Y3os$<mC?e=bz
zS&vZ+4Y;~DZug+#12S&dxY=_$9Cy2-V<V?Syp$cW!I>~G#9doAyC5ENL80ApNsxdK
z!Su$OhM*as?g@;7%3(YV6CTzpsMG?kjbDQDH+8vCIThq%qXbU~W2c2s9IyuM&M4^D
zM7`R0q4kRDHz}%L57i6>#sFIhv5^AeiNO=9l4xnrk_a+aJk$mayWr|LJ%+3s^qh1a
zmh~kU0kkD9MGM?=A0NS(GmkC<Afg$M>X3fk1<=jr^)Vq|IR=tiFgC&?5zE({-gZXq
zAZUZe@yH41xp4GM1X;dF47e`j?c5b2j;Iv1RYKlKhF`$>Bw+~N3l9?wD%7Ch9&bRb
za1j9D2D{N*KUd!{eJ<&^5u1)B_RrNfCo5-l?^<T}rt7=2_1*XOf4KX@Js<A*VPCrb
z*~I>wbH&V_nPW41ru!1TxoYRkV0PuEdxP06eW|8HscQV>>~-^2DEC(!Q(K>{X-RHN
z*K`6-K^91s2I<m)hR_B2Pw;^9bP~RcokqeD7ZQ#=ak6~9$ov(s^l0vK9Li*cdZBBi
z3c7TqR^WFW-(A7Q75s&^B$O82`iXDSDS4DqSWqHvRC3@2epyn*x;lm;VN}#2?+-JP
zmQbL<#YD6~JtZRil=5kL{dX$!77#!r#=ix+m1mJ40@b_(LJ8<t!)Gqx1HT#K2#qAP
zut3ZR72{vVY!yOSL(2Gl5I)hOo~SCCTp+#gK)NTV01$HKth=@2=8hY?rgtUST+2F`
zSGFwMoU>KG8Mzv{61^5pR1`SgmJHjH+&(*yX1lX&cZ%&+aKiW7p1J?I{xo|e%N|Kd
zACY({=cWm2#l`jH8qAe)oUia_Fz1xHAcN?%b5^Mx(8Qs{{?z%T=Fo^NjV&y4Ft;fW
zTUz#o6qFmC)W5G&&(5NH6dZq2F~8-CwZw_66dIjW5HuRd#8F{eaPj4m4_1&;M**WR
z@A`nu4Ldkw7pG!TD@T$lrKR&3EEcIqx=N5YD@eIC48&7Z9ONy0Xy_GxJa#@5?Y~p4
zU_oDkSR!`wTQOaoO5&EP;J0I{wFsU;fU<g?Q;OA^Wk*=eJ0g(~IY_g>7wT6NiTzO2
z#F_=xp2XUI9})^b20#{{-FfY`Y_)ryv6*Xf4UOO8-sax2-L@qT&e@mUFi)EkyK`LC
zn=f2_;r_DqY0i`72wMq3hpqx;a86qv*f(VC8*(d}Z|}T!a=wD8>3+=UYid72@VLTI
zW&4N$0Ma)A3j;hRh}rEKu6<UQ=GJAobt!J0LinDuuesm4{eI62k1O<;6#(e^Re*&;
z5_=a|+5Z_<UAdKgxk1SUs#h$mAQ%<V`6(rVRgCze$Z7M+I$tP<uKS?=Jjp3Il%e3K
ztY}a_0qgHP$-!606*|6Pg?iO~1u+W60?G!d+X5`9t~8F&q^khA(lA-UI~Q55A+FHW
zY5GHPh_Y{Lz3h}Rj=MSqr)i66(v(!tRw)(Hn$m}JbabE=In*Y3RiRY#sS2fXyjJ%I
z^_SCxO2sSri;@~ob)VF45Adg{jHZ)0N3{b>$V2DYqVpKFDdU~AYfRiD`zKB6l<KFX
z;w{Q~)P0|-RE~O5K^Np4lg7zPHI;%fGQ7SMa#Fb~sh}c?oXoiu($l2jeU0!&D3x4-
zg7!#xP{&eScXWlS=0)cV8kK$Zuas(0h9*&nD01pCE9jq16L%=pDbGPZe?(NIN`&gu
zvXHb&Bz{_UQd=2K+|M6{PJO&;Ks@<yJ~ZxXIn#myR}6$N&=7&31R~jKNHpkf7O5p+
z9<BXjk;nnyJK;b^-i#b6(J>P}H@^vz6HN+_L^W^4=+kgGA{2=7-5A$~06CC+1*oU<
zRgu_GAcDLro)5^|_|OQ5mh!V04UHo@p?q~AUJ4*YIbZ2Vrr7W2d$90!1c>*6&5iSU
zQ!oq%CgQNK1~RO_&woHm!>dqv4m<8c=lvt$p*)8QC@dd@)*vXR0&wKx6%_nnSWd^r
zqQN^Xk^2fH9kPflMc#n@<cG0hU0+^5Jd)QP5Kzp-*Md&m7(W0ZPa*cAs_HKxq3|&f
zGZYRBildg4b=_QB_d6r+j)3aBKg+FwExubTZmzhoa(X37bx4o*T|aj9g~SUpdvEVU
zt@mu;9bcx&n`-jT)vii4Kbx+7E?fItihB<B-)|kddFV#}bbrG9u-g9S)YYjgU%vL`
zgdyi_NSHzUKF{dP?P}J7tG;fSHq97ro05iiO=+$@%e5zTzcMklx+^EIolNL+tmRG9
zRnib>Uk70jvYcD<y-us~<`}c}&8n+a*Mqp1>3LAUB~!m8UB4|`zb(yf&$8Q7>~<_#
z)W)AkvrSpHDaAG|6n5-N^|k6m^&Dsay5sus8_TAbT{(R1@B_}3;aq91CCjy-(x0g5
zn=@>4QkU$OHUCy;%Id;a_dVd&XSnsbWh<tq?ll6NROJCSNyXuqkU4^Mtr@O08NOGO
z<~C)yO)2tuC>Bu^m1`@AOKS>EaA#O|n(fT8ohj+F0H^e}X-V?AE=jo}m{!!Ou^1?^
zsN@6~koto<1t&y9AqC~Gf;tr^M5P%9<O4MY`if4eP*c_de(00vlx1r;k%~Cw4qz7$
zlHZjw;hqJqH{$%X4a!hi&MGZAsmq%(EWq^`Cyiyf9@+{O*CU52xgI%`_Dj{`@?4Lx
z4A-MM0|PYGSaz3}zD@O8S}+ggBp01W!wu`0xLbNpK+lcII{8WVso#aVu4KKOPWGvD
zOqrD9Seo;htN?xq#z0md6Apy(JW0xn$h8o@mhv{R<D<$yRk^^Nf%7HjXxTC!#Q#zm
zo@`RqCHa+oIY;@FV1<^0nqomBD9N$JabKk1)T0S~E`JW7{M*)*nSX3i>6Nc{LJcmd
zcICHLQX*|B&COS8%Z=8REmgw>lebD&T5`3r1(Ls5YZ64gB+cb-p(z!i$u&}_@>@L<
zfjfjPfwJ%AfSgm?L&&qpu~4>jvHer!L48J%iyxAM;{6Ig@{R?k#?NA3-IWtxr<`Va
zP8d%<$Xu}E4DM&4NL3b2+frb9pBHtugy{_xF};Iu+@rF<2KMiBy9qDLN1(qVb15^q
zGE0jTQf88Q0Uwdg<)a8j5yTMi2reKX`Xs`!cHkr7SStep?(zuy7zVw9U@d@rJ@Tl5
zAw0_pY%D$EAu6Gqcp}t~x+Oex2CQ%(J|@7K6g+^7+&M6{76DFv0E6uh2TSX?3P(Gv
z<k(4pU;i9dKaSuMf)E0=W?58mnHBsnCu+k*mYAP{Ecpu5NWX@e@x&itkoi}^Cm@9e
zwper`bKDC-6U}{!vz1-yty<g}#?4gWF97QjEm%Y2t;w5{H(r~5Ex~;XW_YeNV>{R4
zdB^jvhbp#fuSK)A)(19E#^y=eyjhz!QBjupm6%?M2^O_wzzmmVeuw0<FQ1s#y<U53
z`OW1wnx>o5+^Q_MN@PnVPQ5I@>ui|rd*Ixfac)JX_wdXPP~+9XnG^sCGjy@aSvVyF
zhjKobwZgu1o!JJ*4R2c?G<Y)&UeqYsvUc~q&A=izZU7d!5tV?AppTq~+_qAR$iAz6
zC`qKb<}BBo;+j!k3ChWt#x%DEWSbO-H`NWP>a{t0&8@1NRWre~eNEQBCS_lPA>2)F
z=49I5p0&59$md}pStxB^o3*b^+1C=O;{n%^;W}o4EAPy5ohhy}x2{W+p4toXo($(n
zbKWfHO-Y}JWmTuj*1<@DPE=dG7>Wmyo~{!uX7N!?h{)i{F90lI3r?4W^u<n1il!=n
zq%1-d0OjJM(g7=Hl<N$ZQMiK2ree~dCKPH?hX-5&bI>q~smoIha^_4X8|wWx37_Dh
z7m1(*E`b+J8Upkvpvsz}3(X_KaaS-F0+R~d_znxBk-#O|6x`bubDV~KXf%<)Lo={b
z9g?gIKCKF;NNuC)BW*|z9|*(dIp}z(T>}`oz`2(|I4axAj0C{ofY@-r;U4gb1OfN=
zFT&|__@?DagQfbGdt2B@k<3A0lRkQO49rhl@xa-__T|DwX?J2ieqK5w-;3rfJ(k0y
zsRM*!^|)T0vK%HYRs@iQuEIeilH`s7h%?YNIM*l=X4F0NF95h>R8S%d41+`qEie$0
zzY^Z0ACL0jyd@nMNzXxeoFIpoq+|UW(nDszI+&tQ&O%b4J@rievRfx_p1kqW^h*g#
zuC8A3Q0ut0{^t4{UDI8O%EBSSE4FL4gbhy1%<BzRhX2JfM(bDh<?PF6UYM-}Tx&#Z
zX|z4Ivekff0n9V3*+S4mzq<~Pg+~J;A-_Lw@#8sDu#^Hn=l5S23q%Srpa%o{8_<Ih
z24y3(lGvs2*i(KGfeFEj0P=?Z-G_VmPJG^j0GT^-T7m2Y^ZW4;8B-ofGLM5JT6>^C
zBn}vgDLew?5rsJbm+9ZUzQV}PGYCMj(`UH68wLSDgKLIOI&qh3w&C9HJ5A|^Eh$4?
zu3>d@_ghuyvu?S(civz!I_4P)><va33<@^a8oTBh3f9|<>tPE_0xqlZsBWH7z~yzu
z8X#5@=<39z6!>&ojj&He!KiK-)>aJ2n;`uig0500;nF84bCots2^xGRawf=>uTru$
zn<ws)<s+?$#hgzTPuOiPXFo#91Ea7KCTk?>)7X$)feuPG9B;OL{l#zB?|-JwZix?_
z-ca+!V=sHY+w+sPUw>vp*QpnO=>O5_&z*D*Y#aY#@A>Adt4=NJ84n7VS66cDFYeg?
z&)fg>!s(#(IOn%>f6eBG|I+c(;N9RI(|<j=weK6Fw&R1Z{$1UR>|dPwQG>1TgEjwY
zf4pwR<gX6B+x&~_-{1W9z}@XRS7zVI(TnRlx^^*tzVlBH{Kx$ppT8OW*P&WJTl=l9
zzGvIc<^5k9`tj5Ux`~%w{_?UDf8y^x+_m@0mwwRf?S5s8_24q|ADsK|C)y@|R&~|;
z%Ms^^pGF#v|7^$BH9vnfs^4sTV(;j!J^4)cUk(1VUf1(CXPq_M%?HeXyL-jxw;P5|
zZ{CyMlQ{R<fqyO7()^m?Q5K(Y0|#C(UfH~`^-90HlD`F6_!$ImA$S{sykx(LkGMxA
z&N$q$;r~zqdttr_>)=Vb)(p7T5a3#4rL|_=dU-GYiN#<Er6+C>ViuaXzRI8pET7n5
zB|tN=nz9h9DJ!v>;)vB$6|tJK5v!?cVl`zaR#P>^YO1ziHDxYLe>f5T&L^AtWh_8R
zVI?O0r!ktEXeBQ-G`{c)eZ&~x$Qk2em@osng3{JW$b=y)2O3uJi4LT1$*b6%RO(<g
z)=8k>ly|C09A&-86C!uw$P==W@)NX7@r05Wam1?0LZ3vdrhRU!D9NsY2ywTh6?}Q!
zXkK?AuM<zdiIOl%u6c7Tg60U9A|c)l&jLthKv+d0fdpovmcDjs#q<)+<6{uHuoY7s
z+0&?b`d8yB6wwm!N2a^Vva<rJ!T%=@q0(}v3;5N__fh%;QJju2Xr7_0yYMeMasN+Q
zf_#oZ`PP)WZ=4f%r2@zS)uf_)8o|V=wz#-fVAAQEm2(gcC4I-*mM)No&V(i8u}bw8
z<x$M<GMaLYJuT7FNyRZ`-T~FT7LP{Z6yFJg4Un}1KJpXT01+BX5e+S@Uv+F96(K6~
znEl6(ym<6@?=jhkr<A5eN|mofO$(alu*jR@dELBi6wc=Yml*=fAAA-Kp~1(aCPrm;
zLyF;_gJ9KaLr!kOA+I*z{6zztv$)*;3Sxn6W#vQYdb6V-0i&>bf8EYBw=2u-N^!f0
z`3xj&T)BXqOh=k^XIXcObt8*wzG}X{cG{C>U0K$ZVqG*WTicpu+p=t1ifyBTs)SFm
zl)2xwHO+3zvfEP9M_lB%s3nJ}kSuZwpdUoFaVZAA6=oZB$tt2tm~DJ&^O;X;CZkgx
zS5>s1qV=b?pKctf`cKiqQ}a9J6pH%ka+hQANQQdxqq19hpiq&Y9s^o9KQWs@)~evZ
zY=&-+Nn!Y{RobiDfm#KAFC?32$3+209G+^LV&`Wez1Fsb*d@aS4gB{pQe!?MMiM&~
zjY-Pc!np-v%OVO^JD~tky29-kQN%)<6j8*YqAcGBVFGX#KuIN9iXz&CQ(oQT@mykj
zg3DVEfM`Uyn)}Z7jI}*wZU00@9aXhok7cS_Q&p{Hvv*{wI#N{~B)-TN1#Cv#nJRax
z%3U`9)8ZGjIob9JZBAZ;0aBBHaoZY>@d3^N8xsXSkvG55+C;O(r&k^wp42O?QPlGf
zOurQCO}k2_3_-(`ak2ugD8Tnu4L;ojtXj~~8NmlzoO0|RSA(sLV(lrm$~D`)QzqpG
z`J_qNx=EwDClCwQC@5`WCSZ<13CfY63<joF=ZB+Wm2fx!rzxl!CMzck2L*`b%opI9
zk+<Q4$XuGlQ%6K-A{H8tevMg?5p-Ml@1P%fkUR&MyU5lSIEML(NGQr5#}oqyuvPpC
z1Z@D^g)=$tf<JG(09vn+yy?O)?xo}{<ie04IJy&uD7-j?|Gbp1#Q7XA#8K1x(0G@^
zTw@wv(;UQKW*)+Uj}zAeS9~+O)7;90ZqDw^+E*u6gQ+J`NGFVQHH|Z^N!{&^bj_M<
z&6<R14h&f9Zuq8spewRF6ULmQac1w0op46h+yLbE`h_cp5@5_)1EqpN$GnkoE~oMH
zCTbnBJYBy&TfbguAJUty-<Yl6c<=Kc9{ce4hsS^TQo8;*h!>mG4mM?WPuA{%<G}Ws
z>$`6nuN!A}Cv|W4&h$$5As{%`I*`>aZ4`oIO=#<L0FkyPA)wf47+O5aTAT<%H0e+y
zy_j@x+!YvXcm<-PvrZ8PD+kk{07Be(0%2w<!XOQ3qYJsg2S;w9<U&e(HlS6Q;vfu^
zC|?l)X&J=<W5sddBo)RAm6I<`U*XWmW%WT}4dsM`!pbsBQv0}qvIi@Y3JfFHTvFo>
z<8qgd4KUlr(uZJ5<$W3QLC+}$0zc9!;^HeLU(vh8W6uilMNSt-by>lwaV9wcpy;8L
ziU>8d&L!lbZ7#~A9O)Xl9@(emE{K3)km5@ps)-{$DmZP8L_jet>X4`;D9HO9(r(5l
zvooS%4Du7&L!#QsXyX0}&+~z3JMN7Xc2hh8+|A_z#)jNA1(#Qe*LIBw#L9P~`Dl3P
zJTWPDb-X&|+OZRq`UQwGjoa<yZ@}R2A0W7k;C%!w0P;2Bb~Wm@#{}5+9Xc=C<Ql-{
z?ktbPPPF{up;Dp(9~1cRVlW{b_?|_Pt5#*eA<9Z*qaEcaaSI!{HMdEzT~G;P7?HZF
zs1!yXE8#XO;xS-@1QX=L5tMkcd+<zm4Uk%hOxbzSj#*+GaUKYl0TBMo;k@O(>Aul9
z-3faGV7Fm@+x(XGwlz_egIxm}^U&Hbv-$SUjI|?W?Z`E`ZXZq@&Xov5dCqbDiy14N
z4Q<KQuK~kAYpv?}VM8<E_Nv?}u=|6%33B4&!)nL1*B(@RGu7U7b$7P98;*Tis}noO
zA<_PH?U8Klkra2NuwT3)!>*X)YI2<C0k=8BZO+-3&8#H*7KMY5kB#aZ3^-=_st1kk
zOrv|Y{oc->wx=6k$Tq%k#fDS{PmmVhx}X*ki?37o1XnD4H<o6%X4$PN=_AUG6bHh~
z!xu_u+tDS^HWdw66!n4}v{ohes8kclv`vjS&`y~i!8CziU!YzsN{$dH0rsWHX}L~y
z*@WIuDpOjSq`YP)Xjo(|&|4L)VM^1Q_n^Iq21EzD$qEAnCDjH$#r@!?T!4B{8Ah4P
zmvM#ksIG-v$Wv09jJu?k6f2j2l9i?r%sEun6Zdo@k@q!*9O}10XkVRFn(`a^OnX+8
zM`>VK@?d01Iwrq$#hA-=#y2SZxR+RPanyWC{;iS^#tRViT}0BvfkM=TR`3(wptPj?
zhCad2GNDs%=+pd?L2Y3#m|jYO#&KwXTe#GsByE!lr-kuJXeR#-fQcQV1%yX3(GL>p
zHtOmK#*p%Xyx`VaMG)FX=-EX5e!NOjw4s1JK<!94?}4KG-A&@DF+!gRr6SUe1(Ntd
zoH+|;32;^uvL>qBe~gbfisD{uH~QKTv?HJ-j^Bol<OJDT^gV;16@iA%?Z7aib0F#l
z)HD?A)<ydT6Ep={qP9o;0i>E(DJvRE5VZ_av{NEQL*YSXgP=A^_&Ho;(Tvyjq-#2|
zH5~~s4XC|7cooRm%qz2ox2I;NfaJ{Ubu~?Y-_-K<zU0=o`fv9?HdZ)bLmU9?B7+Sf
z-VTJ<=@jTt%F+42u`%P=cyDXk(UW!bzy>;ApaTHqDGQu9D!NUm>}5d3r0BQq0w78|
z;LYH=B^2u)!M7x7jABhFfjTvARMIIW{-yK>jUj1kz>h5<PSm9oC{wjk?@??50M`&L
zLRtc9Kye=7{wc<SdS$JIq76mVfXYUil9WAIL`k5>QD9~5h{@Nj$ubeN5Em<PGUY0L
z^^dwdFghqtxzOmKB;``oN2MoKD5YMiP)fU0p$@6-aB2(Q)Lr_vh}5Xi3`^<A`=Mi0
zM4^nya(O7dT0$O5uS(=;kXt1Cpl@`ZE+G${Z%fESXVwz(&{?|ZJjDhTcz&SZV2&s@
z$)r%NY_6^=sgU8z<sb$XbXF&u<aDyHj6nsR(WOlj;Qcg9Wt1?ept-eu08WmPjUmpt
zq%w#>1<jRQErlz;Rr9??iYa4Iu||qle#Z~UPwLSmd|bJiSpIG6l1Rdf++;a}3S<n5
zTuFn9(&d&ms3=`(DT4}mF0>al0|JV<PGV3&bNM@@T*`NmbW5SiZ#9mj1{I~>%b=XI
z#CuufSg6{x$o{GFbjq!eeeo|S{62;MW!bN$V$`5wnli8DIccTM1Kjy0Uus^;pkj%P
zw6bj46k<=ozYdKb<i7$C%q6^9bBXUjxX9<CZe273{x14(rio_5=h26YklatR80(4e
zVdA?8hyv*Y^by_{5k3C~f-wZIARs*QSJ8(rpZ^+y-$Rf<fI<v^1p#5GucD7=ipJ4*
z2|)+}3O*WMn*V(WD`m*=A0T`Iy-;Ca@Ck5aLNBx!^g<l-8Cf!b#zu3G=3KdJFn^;)
z4>_*-vDJiJIsiD{3BZO%zm}_(>zk){rrFh5c6EwfjTXHmEbC}f+?!bl)FfPrIWXJ=
zx<s?#-<eYU|7}WPfK`b2;K@G$Sc0g8*8);uNmkq~a-&MMO_jv@iyUsTsJBwd)&i|F
zi7hX3OT5kOZHlGK6O5(aX0}9{p(&m=sGmpL$A1Zc=8(fa2%;8XMGE!8C1l;eW%H-7
zghl|WBbO!!Ag}y)mxRGc5La6BTFjlnq?fWV|4%Ln`vdy^%TGw@x-Cg#SK^YeCC))@
ziL?>qZj*jK2%YB70#GZ#_CrW{3NGEqwF_pVbR!qX{M)&a3zZ^d8U4tpJv1X@#^1<|
zT)M@mipafLxb%zMdk}(bqUffS_hv!Qik&7sZ1>AsG-YoDtCY&o+z3WhE^rTqMr4P3
zFsPD+C{HNHhZ<2!OdR-W*=n)ix9dhOCF+ppN9hd{^#~WKmlWG6!t&4f-mUTqs>Rk?
zUR`A>bHjx44FY|?OKCh%{PKCI@}Nk7EuRNzA#qE{LucKh^Jq4(KuNnrsu$+5X)%f<
zbzP+uN$Odmxs!~lcI29&?(%n%(ZeQ5s%BE<WX`#y=Q4Ma$tA3oQYycJpDVf#joeB0
z3G!*~Btxl~IOQ5!<gF8UtF39`Hl=tAk=-TUNw#DntS+B6sc;|=Igi3!V`~+Pfb$p}
zL4dj<QL!+BzV9F)duQF~YeQf~5JeC}FpA&;0v>^Y0B05dJp@P-w5o&eVpux@Bz+~7
z2=YeTMNA@V2qxgM#A(6<5WEl<Azt-0h+M=l0HOx~^KrN%{^?hJeS+(|@U;J8uLmom
z94LF+fpp7(bnAiiE!}#s<^k84;X1{eNQ*TA$UEVEV{JK(+!b&=K0TG@TC-ehii6+b
zD5Vp?6D$j6`K{Cr{B~X}hHM65_<jMNC5XzpB&08P8ub0UTL<umEQtj<6>NS3w+<+g
zH2E2jr<|4ew|VOT|0c{&{yKu207@GEmp$!3-r3gxHS^!V0+7e*cRNa&@O~2$5V}IN
z(L^uZiD7#Xp!J4$sN){`et-bA#9Dj%gBVte0P7O1D8Ge11V@2;6dr=-GW}DS+FKZI
z)8)OnYRBaRBB*P;+&^!y7@OxA3L0vR+vgby+Un7oo`TJGG`*)_?K<POd4_^7=$g?u
zo`OBP9cVmH!K6-McQ4xDFL$EhJq7DGp!Gck!G*2uvCBj7Po2a(KUoVwTBGGR82lkf
zfX1K!V8P~|KMzR@miB}AlM+LbfFQW|5S;UbTS4G=GVsC+ywlW$mzomGdia$LdQ}MA
za!PFh@eYl`&(0DCrlh6)7812|OZzR-uQjTSS7GLfco{d);b)3fCd9<1Lf@aqUm*Q=
z5D#DL;7h<^QX1eo6<4-W6Wl+7qJ=6*2`0D>w6J9p@F!~2q;ZB=2K|IG;IRUr3<@m~
z{P?M^pZ{+V&Zht<>qT~J!lAC0T+;eIsP+l&fkL%Fz{eBSORSR5V11=3h2L3(dqMCr
zj0shbrO*$cYW`=KTU^GaE%C{cCEVhDOsZLmIGf0nLi1Fi51ua5Ke*U~TH@=B^}w1)
zz@#%a;9~)39%+QQ<gp6(TJc(d<KWL%4hMu`_#IgBj<D*{!jHjr!LK)s#G>MHF;r%V
z*Lxr(7tLdEl=G~VT(qpQOOIV6;*Y_Jm+{uqV7xYr-fJTsz&46k0i&F!*#FvrkIy2Q
zMeuzD<XBn=eYl^Ncd8Cz7nH$y_3>;%bND-$=f5DpV+G=U2e|slP;1`7$LtRA0+e0+
z|AZtsKLsnijrj_lPB-6GtuxFs2wE7O<+2(7a!f-?{h2pZ>fonnKRaM&bjwprqw<qu
z)~3{-9OF%?Kl8PW(Q>&r(ULg*`r$VYKdv(A^f`kgXIMr)^*Mv{;X3bZ>plIwJ$F3s
zd(upMO6N?~x53_-(G5T9JJUMTn$#!vyydy=fycC=BWvg=6jPToG?Gt4&QL#ZkbYt}
z*SKn?ZiY{8d~4<HmE@Oq;j%8T9!}4B^=YOvrK?XhtedxtRp<cMmxhl?_(#gv(Mq!p
sXhku&e&{iVek4aV95!OnrQu@|{*f~Fl*Oill~W8JW7tO}BFIAezYKys$^ZZW

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/message.cpython-311.pyc b/paramiko/__pycache__/message.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1a746fa86fe49b1f47853120508f4703e2651874
GIT binary patch
literal 13496
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
P=JdZ=`R}%P%jWXG*0{c8

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/packet.cpython-311.pyc b/paramiko/__pycache__/packet.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..aaa5448f0aab738c25a9e73bbed9fd57216bb779
GIT binary patch
literal 25065
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/pipe.cpython-311.pyc b/paramiko/__pycache__/pipe.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6179797ea1819a7a657af6fe4e6caaec169b1ea1
GIT binary patch
literal 6790
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
Y6QS3B@)gy6-Dnt$i=EqopKwO~7l8vBApigX

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/pkey.cpython-311.pyc b/paramiko/__pycache__/pkey.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..89f61c7b4b36da420774383e15356e1c1f2224c0
GIT binary patch
literal 33030
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
XsiOO`PKO1sarg;~|7?>~wnY9P8O7Ze

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/primes.cpython-311.pyc b/paramiko/__pycache__/primes.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1e7574750adaab8b011125b086219f2cfddb693c
GIT binary patch
literal 4915
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
C>1xRU

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/proxy.cpython-311.pyc b/paramiko/__pycache__/proxy.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9c90951459940d2ebd66617f11db143a189cea87
GIT binary patch
literal 5414
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
JNPmX!{{U?(4CDX+

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/rsakey.cpython-311.pyc b/paramiko/__pycache__/rsakey.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..267b4464adcdbfd562dd450b437575a301293871
GIT binary patch
literal 10053
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
IawZD@19YT|OaK4?

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/server.cpython-311.pyc b/paramiko/__pycache__/server.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d88ebaff20e85dd78f7fa9f8fd10b3fb6f9c9ac1
GIT binary patch
literal 34312
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/sftp.cpython-311.pyc b/paramiko/__pycache__/sftp.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d3f96961d6cb036ecd830a58ce3cf8e638c9d794
GIT binary patch
literal 7975
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/sftp_attr.cpython-311.pyc b/paramiko/__pycache__/sftp_attr.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..51edae30b21d17693ea887062cfb8dfa87c0e390
GIT binary patch
literal 10463
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/sftp_client.cpython-311.pyc b/paramiko/__pycache__/sftp_client.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b48ddb93a0e02c837878315fd1f67a08c77dfaff
GIT binary patch
literal 45270
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
Q+-FQ>aG%-oh+(h)7Ya<OzW@LL

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/sftp_file.cpython-311.pyc b/paramiko/__pycache__/sftp_file.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..eba11b42b53d8243e45a9bbc011afb445642e9c5
GIT binary patch
literal 26361
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/sftp_handle.cpython-311.pyc b/paramiko/__pycache__/sftp_handle.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f29b2b213c110e45bec50cc81d75c070e60bd1a0
GIT binary patch
literal 8588
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
Y@_&n0{}XSpkY?IMi(P&8Pt5uM0Mx_IH~;_u

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/sftp_server.cpython-311.pyc b/paramiko/__pycache__/sftp_server.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ee69cdc99ec72548f22170cae4116624c54017cb
GIT binary patch
literal 26615
zcmeHwTW}lKm0;uDAOHd&z_&z#;zNQ-f)e$9o8m)~MUjd~B{m!xq6koggon~->cN1H
zX3E)xRhpP$Q6t(VSCB2+tE}xA)zoBGv%9HvCdnkVAG6)4b`jfE+H%#FKk`SHld4oc
zw)ULc=obju^d#Qd-P&gB;y%wk_ug~QJ@=e@TmRPOs-oa|a&UjNW0a!)2L|NFTJn5;
z)j&}XsVEhuqK2q3VF(+@uQ6;SzoxJWeoYB;(h{~LtzoMH^O+O2Bps%S-;%H=tHM=D
zN7#{chMh@Q*p;jfS0~+JH%Yf9JV|fZOZ>J(P0|<k5kH;qCu_sC#BWd3CF{fW#9x(Y
zNH&HWiQfURo5D@W=5TXzOL$9iYj|t2CESv14Ywu(;Xtx2+?L!H-j-|+w<kNo9m&pc
zXR<5YmE0cQp4<`Mkqm}|$>+k)CA-7j$)0devNznD4246<zHnc1XLx6FS9n*lKir?(
z9p0VX6W){D8{V7T7v7iLAKsrl5I&GR7(SRh6h4$Z96p>p5<ZeV8a|pl7CvU6V%C@?
z>U?AjA2(7bsi^BUDq0<@iq*eorl|MfpY(*EhrV(r29hVjC!jsX2`cEBZ?2k%F*jmN
zZ!#T?1tvx&&jm7b*RQ7;HdOT+EH@T3il$_AuV~K9PVEXBMDrXQPXw(ZePSvT!vdo1
zY%G(Rx)Kwe7>tYzoF2V!epsv{p>xCM&z_!`I6XcV85$ltJv=0ON&MJ&Wa7f$smRFb
z(P7aNPqF*<0B|3sNf@0@u`y<5YB~nd)fhd%vP}H)92?6(jAdx}#D$aK@l4EJ&fLzh
zvE-?#R5TG|AkJ_(*eO;Go*jyu8W<Y__;ym|(D1~d6g@FHFnM7@q)GJf_=rej=COgZ
z!vGK4aAII6a&~xPV&LSkSc7T9FHR1R4Gj-P&JUj(eMv$nGBP@F5+VkML^ml#!rwZ7
zdQt{MA_mT#gQ6wCBxVrGKpst@$@3S+2DMO!UmP42?J`z?DtgBOkQ6yMIzBNh+DNVA
zXQWhu2+21zFgYOQlaL`@aenyh_&0~8_MaanZ6)x}o*6oQUdny`EP5SsRZ0LMV}Jn1
zzLT;KOirGkki4U6><IA^AQQurB;Z7dBf1cR96ddDMyhS%rL!bJGD6bmxq-=3A_>VI
zFOddxLFB^N#D#O`#?Ma<4+V`3k~d=lXBw$hevBp0=a0biuYsk+C}1TNvXQVUW}X4&
z@|q!R$x+}ly+((vQS)n5*haz@2-7ip)cP9r9+ZAxhDn91Al9}n))A$ltTS2@tpb>?
zs4wb--)fwOVRy6|Tu;;ut~crdw<hWZ*O#Mi8Nz;;f_||IM^U2r`A@3?@RX)xCazA&
z5{)a96Eek9)3E?M8_xvboeEqEmCuArJp@{u4NOm^0+(Zf9WX;Mb22uSVgqa%;(~!p
zj15d>08lJ2E6t$5OqvN?y0k4b!(QKZ=~94+eP=F~VKX5D?m{L8bxkr;sSIHgP(G58
z=U}7+;u1oinr7oSrq~ja(}}4}3B<HT=~IeA-tUHgpT9zQKMSt>;A$W}S?WFTzpsQ1
z6sv)u&6hP~jaieP^BJhDA&)e&?$HnMf{nGSS*|KIWNmqQg;JBjr!O5f%wwDm&ny+O
z$wdGPE&H{<dMHpr!69md5Lq=bf1HAlLoP`|PB~<xvgWMmiXm(HH-__=)B29#2L{OR
zl5<2&Ky2ozzkr!Rh7sv(62^QwoyxGx+%yYgj^nNpV;Q9Y5hW6_Xkdm(Cj&R*KzJ!6
z8U>h!CN3dCO#!XSR9m8oPyom(L)IH0D<sCL)a}4jG#Y2)>C{w0fpvX~nM%fh|7QY#
z(zV#_n`tH*(8NwLF$iWdGCoMXX(oOp4p0c9Kz|A}4Q8Rr)EcT%G9Mzf1VV#=b1DYF
zq~AlbXI-@#=$E7!f?Znz16><PK~oM9H;|qo^Q<(X0C}a0)|CvEt%5L5ah*P-Ad0nM
zv!(bz`tsG-G|uEqdO8l1E=tA-a=?5qwSn}wQlkP$MipjzBoo(HKn9V_T*Mt%jKZpv
z<(W+NIbaG%J!GKcsVjkaDjL5LkIrcZ6%p365{#L*gpkkt2B-E|u$sY%Ceo2eJQZgn
z5e7Mf=(++thLt%EgY#0X!nj2G%9R)+x=a2@siC5^l;FUG+1Rb<T=Kf;m`ebRNHlhN
z?uzJ0#cn|oVR&NIOgs^buv3>4Aj8b06VVt`3R$Jz4bmX~#1b>2O|HR=qsj~+CN{($
zdQLP?F;_C8_1aDHYgZ7?;IzlK;`4_~eZWDPOyAWsb0wCX0)WwU-%xsbE*VR)nZ8r8
z>+z|+iC7XiE)e;?zUxf-DzxG<<cuZKOdrTUY+oXNInzfdHGVDKCm}%SBXs>Xa|o)N
z*GN(!xi=$_kojwH?ohuAQFgCDH?DN>bgMwOa&+r6x<R1Z@*R(DPyA1A@boc(KE~0<
zivH$Rf3V;W^8RkY-<`8Rt7}?L=i{qg#|vG@`K|$>Yv7}keAgMiZd9ln&6)2xip?FX
z%{vRtJNf2*p}9Y27U+gz^A_^^NfZ3ua~HR?V6d@Rw{`K#$_!VxFVBJ_y^n85zX&S_
zf1d-&)U=j?BU8oj=a+$Q9vB`{+GT^)<jflN44GBLVPMt33*VO|1&TSHH4af1hZan%
z!m`U*d|p!;agv@aY*Mn6x?F5#vB0GwYD70{BFlyj7Xx)~I7&@W|C=!(_~?5DHVS}8
z&@?u0eHm##FmJpV2-?J|cm_BL;S8dgy?s3<niKI1D_UUp05j5zQxoP)dit73<0QoC
z60|VG&|uM$h~0=KU{{g3A{vsSJrW^sB9RQvY@8%_7}z0F%mg^|L`t5Jh#R5)8AN9O
z3Y_2Hp`JNvA4FGn-;XcH-}Up3F2T{YMw#tnhGK2Q8f9>uGf3{D;hCrQzHQm|(7EJX
zxsX5gnEl}PYTrPiZ-DO`6#54Fj=_(je8&sF{s!MM#(TyE&p1ckHIoYEcUH<(^ZLnq
zC*K&oJGxSvryuY6;NWWa(L(o8zWcb)eVh**|EPlxjQrZi2S$14S;2XhlV9d6pggUm
zALI&zhW`=Vhd>9gTtk%fWT`xI#C4CBexn-*H6Z>#+4q(5<s%BPwRk9X=milFn!X}U
z8z?*c0d&p$QGk~TOWy{1UobCNQh(V_u^u&72gS5v&6{Ywsk~h;5n5$ybkHs@ST_aD
z%+{*qvQ~W$R+^!kfy&maFs~}Yn!cYawPL~gsuky5ltSN4!%gaz=_Tr>fo)ddWi5If
z7i=J4R;%f%PhZ~xO~wcp)^!gEk#yGf9?;|a3J*0<sU;XAeXo9hlf7#BKc!bmn>BQ%
zHa$zT+tg6`XryfIl!1DQN|~e)KpMj_NzvtFvuL>Yy`Xh$zI}q7Vo?6VRaueFQi>4v
z-8N=na=sR{4uRg@Kn#RnkgPJGK>-nXzHa!|bnH4x!vT0p!p5P%d_au{vAZPlfn+d^
z<x*D;1%frqB(#FCOa`eyw1PaAoMJ_PBm<RzgW_WZ{$WLu<l-(>8_7s0i_QoWn~FxR
zPfcHou_6sP4l_)e5$#I#q6x&3k_5(HhJp-KpJ8yui#BBRpveL8t%Mo?lqzSZ*traY
z94}}mivV*Hu|0#%2$nUlqB%OnPKkyX^DT_1E{Tef)Lim1AdQ<MTpGwjmIu*I&{j8}
z^G&LF8Y)Z6LGerhSmGTD1R4-5>_yu7hW)PnK_}OEY_)N?&^Y|j6~1vyXdL6|ae*G^
z=yAxhO1Bm0HlA)5=yr~7hnRbgRl2=ExASzTKzDL<=bEwB-neF@UG>GLmer>2LQ^;2
z)GIXg-nT6p7WXXf`Ta9r^BQGz)vHd?<9}#hvcKKQwH@F+2L;bT&U3Knsav!zTA%s0
ztWhS+Ejyar2Y)*Fk>T%0{?iEGc7pc|3Z6mEGpMA$>wl+}Z|fJ@`gzZ8!Lyt5>@ERC
zz>C)3!?R`su<-jkn({TRblmS<?#&Grw+4Rr@>?&zdGXQ3+}N7Y?rKDst|iyXRKDej
zkN4~qJbO9vLTdm&o2yZEl=`{0!Cy}Qy6tB(znJ0MMtRR!!E=`LoK?~*;J0Qrc^ixV
zhE;!O!QYur{UpKr2L=BiN8aDB84V<PaR_mjpEWns4}s6NLf2=`!Px$+vwq+x^|Pa$
zLmtyVblTAk4b%@gOuukgAn=R*^}}xJm+q>QEv8>K4%DA)H2veQ=94btubc+(|H@@N
z>9zdIZ9#vd1u{)5t0w?QUWorb|0cwuv<WLC1f{3)N~PzydUb_9Ew6A|86y7b^N_Vz
zS+nzsVq)VGAj#G(7(ne|)#j1?<<RfdLyMGE`|E%YDhVp0w19+^`f#QDv0*GoOI*n%
zb0uI~u_$vMkQO~Nupi9Ks&kz=fzj3*QwdN_iI(X^8W`Ll#05=~kZG7<UV>l-dsh)C
zQxKSM)^Le(u<u}zx4{A7vNYFq^Zyy`dfj%<wn_&IbbzO~3G_CO-nM3?YU-9-SH0T{
z-tD|MD0qV)ciQWSlN%^HU2mMcd-C<u_fF^Fujs86yxUg2!Gbr)d%Fd1_ZMZ>Z<jc_
z5nf4_)KVx&Qxb*V0}s+C3%~NCs>iNjK8mqvrO1i$+zLHPxig+JXv!!m&=eIFg?ko7
z&cRU7Ff|8=D$0hzbPARr&{L!WF@{N{17rk2GntVTOAvqQ66m&Z*#R99s1z<;Iz3(r
z20^WWdPM~qF!@w;3RFWlq@Y~Lz!o1gTG1G2nkE&^D5$t@#8UAX>Pu(lQlOQDwH37$
zutUFo9o8=eUQAwnL8q!Lvj~r<PM8=w$D~lZ0a!vwCVibjRZFSj^-3hbHUO$0rL735
z2X$jaho-bMM0(<qR(m8>MT%YmD3_!LLO#Hn;FE}712ya=Y<EO!O3^}rZJLFE7Oler
zgM-5p3@)M&7#klRn-p#EI|*ui28T~tn+?n?_%ldZ0cnkOk<Me-2mv(TpqV?mlqXp3
z2sn4BH9zHaJ+Lf03yv+EW6P($Am<DgtKARwE*~jWw{q33`EPvK`9W`?b3fO)|5INF
z=j`~@*UC9tCAuaw3Xt$Y?uGRD`F{tTV3&_^rAMXesNou+S?0y8;i^&^{0b${LusaI
z1LZC#hp#GJvRwWvgQ)N<0x_V2h|&#tvItR8;(}k9ALWvD8FWRtOQ5&upewVAKv&ed
z06~%YXN+Hhy(04y_`__Qj4@~-KpUYiy_Z@rW{r8Tnn(3Pt;&8tzY76<feHWgurPLl
zYNvKl8S_n}BuS%GZGb;aDJiE9!Qd%#aS*f(S771=P&v09^vR0(4@G0td?wPMk|xQL
z0gz03qS0nil?HOP0_@TyERITN(5Zp4GK%&I6tAHM1O+;aBxp2%F=ToI>6@t-(-XMD
zr01?nfy`|Dx@1g2@e_DXAIw9rn8s(~(^G_<bb+-4h4BCzPl761((@wbJ%MQKI;>>K
zHURINv+?O!1uadRh(;SY7+{XPM@Cq~RAh#xvED%MpbGPL091p}gmv64l}P}4P&46=
zh>lXVtcjIas@j!-Gyo!P0%MJmTh<YSIvb%7m9~<IeS$ICW)u5Q0yOx69huqmTp|j+
z4$9!fZL~un`@Eb<C%~2q>U4HCSV8*@j7>H8lEDXVsFHv(d}>kAax<RV-9K-<**zBY
z!A9kFMx?Jp2jE6Xv_@tUQ=p#p5DTnom7WldX%rx4VK_mR%SO-;E;>lA3#W%po*u$P
zAPLb5iF5HN`Mm<aQl5dclc&LKYyqoyGA7cH2Rg6hOA?=*z?wK69v02exhatbdrJ!F
zCPwPZ#OGr{lSs?`3Tk9%Ci5MbZ6)DFX2oc`z+6xR%jj6KF6HM~G6WnCw7e~p(|zyA
z%Kn058|T>eiQ9MYyDO8z)_&gIkD9Sv&pb8D^h!g1&pStW&rZR!lk@B>%DH-ZcQ58T
zs^&WU#Qe7o-g89o9N|1iiVpX^!z+HFF~~cJ#B=<av-;kt#TWDDcU+)iTi{xc^3G#|
z^BCtmR?7USg?DU+jgUR~%v-xSn>X{`PQlxW^#mV#iyrU8s->!xJ&ypN9fD^E=h=ZO
zID}quK11Jcm7tnX$p?FcO3|7)6JSEn$skAjhXc)n0n^U|=D`lz&$k=EomLhRNJgfP
zOTg!+fK%y}&U%r0!JIYcVF=bgSwlI)%$h)0|8&8kwP<E7tg<AnQ&iUhY()su)=;hz
z0yT1P8B}E{stC0*#$}2ofcjz?RAq6ZC{|gHD5`uxVi0p>nfwyW@3KBMM|sOwznV5j
z0bEO7Sv=JgSk}zty?yWR*hV!6s8E%qRE>plmM^MZzqsjZ_;WA?R93@*UD~WKSyHYW
z$L3q-QrA+zWCEy4C$cH3iKGzvg6@)v1&pOc#RB^GI2dlHK&EA|<)XcmvTu)Q0*R5C
z!br>I+h8sdt1wMc)iAhOX0Xkoh3I*hOBnGoMwFB_lJ+NPmGm@mOrAmKG&)2#V~)dd
zn+y^tvVO4Yje-~S_^^CkhZQt7@7IWlO6(_)DdQo0=~;E{>+yT>oT*4V-f-M?EP~EO
z*0A_NbMLa(5GObEsT)jG?^<|wm*587zQa{h^!SKkBFcN(1y4Ii-e=zCm7#ard2g5C
zg=}^yn`6nbGQ@k@1Wy}BUMbt|4=wo~-rFa5`%s5LAXYC`uduwQL-2HP<VEfjz2CIl
z^!BTqw-;ViVjyeUec!$8UWC6->spKcy{rBs1^<z!lfT^aYxB<z{o)Ys9~1mzpP7vH
zHNU060p8-RfeoVFwMsV@z~I)lGQrbrU}@vzmx#S1I@<s;8Du=4e**obXEgsj>sF=#
z$WqJ70J!wvfr(0F39?^BCc|nb8cZJzo01`-G^uJ6^wZ;uLa61qWl&3keGCY7+Ne1~
zVPdwzG@`C5Oa!o0ByF|+a{UQzOj27&NreG!z9jUVrp;<CSp(ak32h3K@*RL;U7Nl2
z-9ym@<{$tJx|nhJAyZX49dwG=shJqW!ZgFxo|vCepG~GNGl0H9DOonR5fusZ7N(#O
zA2h2ITC}3r2-a>;5~H;{gCniZ<XTB<E9Ft<?>vzrWlJ2<>z3s}J8h;24p`>gjc<3X
z+<3F+QO}1}ynDCc-VORpyYCaHXVtm2;M}^BdOX8B_X^IvoO5r{S(6(n($%YULxFC1
z+q-h$hsWPK{&<hjeVA`PA~YZ28;=T&M^&3N?c~r}-LNvi(_1BP(c#4!4i@(u$_)un
z!OUt)sL&GPTXqU9J9+0W&bjN8{sXz8d*dr3t6O>sTYC5{Az@31clMz!xcYAO;;Zk@
zKiRjsd$6#3kl#Hl>>lRn5rH1z<d@9UvC1<w2kk~H-kmnPZl9Vps_V^yDOCfu6lIS}
zQDE<~z%pQ10Q*kLEZcd2;xu^xaOnYx)66$K=mfhq&<4J*Xaq}3ifK%=U67U(5l0?n
z8?ov%DAaBZqY^<P3tV7zWMM|#0x05RnQ1*q5Yn5k*RpkuVxIygmAMGOKxyW1=Z>ry
zO|G_Q^?|&9`Fo4zMKh?3x1z|^raHy?0H!ROQMzkGF^vWmsSLuNr~ao5PoEIz6P)~J
zP|A4CRYNT~+e~>wn@J~{VGj_Fxs7nlFW1nPp9}!IztW2!#wa^|;AUm^R94|W>x8Hy
zTJHEBp!7cclO7@xYDQ0vUk{(yKQ;AhavHMcsG$-M0gBOJr7N+1FOGUS<JcsaVum26
z=s?STMB)T0=V879Y0M+cP(@e{Zap&0k1#d{PRW|jpnxEnaT_O^Q2=1_5TRJ|OR|A8
zf;*p59eO&_Bvc_CeIJSg9pNTq)!kKacjZ~$-6Obra%Rcgzvuqd<*V;@z&1j_orJ~i
z`?R+8{<oLE4J^c2Q{paB-q|iV+c{@@5jjp*!P%96^~sH=Ebj#7FvK~B2+#QhEj(md
z4n2mGFnt2u$H}jxjnb}}3XMstGOQqg>-wi`+)cU_yNs4VcSUA8tZSQ^^OaU=t)8f?
zZrp}ws9MFf!Zu`soL_{!el-WW#jVW9f(2@=Oz9SNbzfI@!!qk~S~j5d^}_y1=+%ny
zfp!D2p;*q~&;%W#J3`h}JTXQlfoK912$=*7?n8)Z&fG`eU!wCD=sZMc1syzBpp&Na
z5FuJ-aMlrdn)v}ltP`h8wpeLe>Zc4b(n<+%V*%`%P#&g?r*75LRq%94JLjC`nWH*4
z_xhoGhgQ6IkLQk)i6AY#AI|d5{ep8p=iE=G!57Knl7#+XaK&=}h2<BN(_%$G7ywOn
zG*#NS<X|?r-x#|)1{-(S@dpJb(fP`8yfYv;1DrDeD7^8)-4_-!uaDmw&%qzDu;ToC
zdq;kdr*{bS4o-f*VE+98Xd2eqP0l~a2feiZSvOaVl~>)ed=`LLadj@sXN7!P?x7%1
zmd}PKf3BM4<QoR21*mp|S*x|KY_t-nu(BqN>_KPEFgv#_*t2%+CjAD8hgthVl{PMG
z*OYU>36R!%$e%XC3FXkz1M&#8S$lc}CETnvYAW%$m$G!_8CljVPVLDN?KzsPQ_l~y
zGPs^QW*RrfIqQs?D`M)rikoBdWG(ZsTY!@;HU)cQ){5tBqLynU#}|&2wpXl`>SDK+
zDuSP~*1NKnh3af|){?d2*`rcz|HQVcC9*a-HCv5%>f65H*5!3aZDr}6sA<6q$HpCH
zHP>XlS$C<v8!%RyR%q(3&U(t=`Pg<%x{uwWhV(hVa8!Izx>8SNYx0V{L4^yFlTS|z
zumXA6?P`wlblguac%fgt#0S4}Tc6y>x}X+~T1kbX?;%psRX#RP{)z40s06X2)Tybe
zufi&9%-5^wsxN9^*U}AgW~(aX)a+ALm|54loK#+&_-aOq-J=F7YYT!^V}GbVodRoa
zJX*R(GQV{!E#FrGgUjqxW|k<l!2Fq>OGU}zI`6xX!eiHHE&y#7ORNjO!TrR%{{$E%
zt_4s%4yllw1oHt1n+7=BtQI0raZ&z56`YTrzSfKWV6${Q{4!hzU~WqXP|4|3d+CJI
zzCFw|v`E?^$IyqvBRb$fI2<w{r%{PP6i%=LQW}{Uc>{9~od;OJl#HiP>Ya)*OBlA!
zU57(VG3E&gMB`WR5I0Kls&pVJ>WE%W5)W{=gMM{yE{Oy`L<sa$;!31+tQJpb!MXNW
z>Iw+jRhO}E;LQ1aj2H?;!!6MUnNcesn!qC(;ViB#Gnb5{=b%`6W(Jf7B29_`43VCm
z1&t&6EYmO;kVIMzmk5|EP;&<70qO(FA4${afP?nX*F+eaC)fOBt$-Tej)KYWV4Vbm
za1~Wk`><uHh4;1z-nN{z=&O5pWa-HLW6Q^KbV;9wTD6>|==DF`y0rCv>vAh(t8IAr
z^3u!qFD_rqRl)Xt#s8?4bL@au5oL;=mX8BFx&9NNaC23I!p&8UhpehW;pXz;e!zpf
zeb@-(3`H*pEOxt(IB+P#?Rjv9bGO1<Y-(P#J*#WWw=Jg@O+`=bqGioSc>;j9%PZ|C
zpq_V~T<Giu)i}J1-?)1?cev=Sd04kpcfVn|A$I~cy>CbJ{cp}bn*Fev_w5&a`w_Q#
zP$aH;+6tbweCWwGZr>T+Gb(sSInO9Ls#UD5Uk>Lx`P%J(zN5Azx;3`uUciv4;ArL?
z&D!=OUPG1f`n0D0{`TeVIa|?N2i;liUz}UHasSmqbthNd`K-Qa<xqa^@r@t7`qavA
zKgQP|7wV7aPJIgT2l5yA`fj1VJ9nz+ugwh?>zfuc_ve=9AY4<wvZGM5jjP#KtZ#UD
zW9i1qOn#QH?-T0#7R}G<;R3;j7QQ|t)Q8|GwzpY1+Er}mdTi!GgMeyn9gI{h9FIZA
zO4T+mjubr&58X>{Fb8=$1y3jE>4b53+qW|LX5*tq-m`7d0==`+dEdQgUGq`C1{nu9
zY~b=Q-g@}GrSI|eJB0cjymLqH<XRi$uGKd7cO8_wZbOwbh`6Zz$?|lEKzDF-2dbqp
z=B6s{LX7LKYw-$Cw+M6#N4F@V>&Hz!kEeOMU!eOr`ITf>t(1s<lm^KeGLT&jECl3-
z*yHOVPE&%!xwI#B)bk7r##H;4=2j$1$nDg@QRFySnG9NqlGN$W8g;pmf01V%xB@Am
zkqIi6P<C8O3FIfGr(y|*k}B7sS+OdXP^7d{9eADDFn_poD21phdo%29SXxlQup<~C
z=3iWA&fR8b(<wMnkj{j(X>j=nD`jv6WL^bFw2nv44}A;ny^cq|b$)yd?)`%A{1Ez>
zCd>dq>2@!J2eU=nc!XT-Wl;FR5V_XNpqPiS@J_FMJk5GtGDpKU5UkRAL$5m`AFYux
z>CUy5rOhCl{QxSsLw(|`dGHFTJy0bQc--}q<NR|ci2s?tZuxX>7^bny_aM!?z}RZC
z2Qc;w#(pY!fWdz32dzp6DCz8NMW^?{`Q?{k=LW^JDX@pblrZmXB`FWac?VQ!u&+xP
z;~m>Dp;apJ67Ot*5^jIaDa{UrJ^_2A7&1GSVf^v@S{@?SKic^Lwg=5@QS<6Fqy|--
z(aSn|nH1zkgI23o+N=Co)>L`Vqn8Llvqx>9?=WrVE`%O4Fa)EzzVv9$5tV6<$O(MK
znjz=MUt;G_W>g;4rt2O!=?{Agy{ui{CuHYlV3E>GPpMx%gnrSOdF9k~yBOFh!Om7W
zbp$)jPHi}AJBlN%;U+31;6dBeP({oTwz4Y#*hAWI)~1epUSmkszy$C6N)UCfDzrZm
zvyI!dL#YtYTLHzd+bJ8^=hQq>e=iys>!y3$l&`qcD5F>!;3<4vt#923+Q|s2y)5Y_
zHX5-nZ=J4>H6v7U&jWW+?D~-hEyXSs+iVrvuZFaHlywIW#@PL8&hj0Svb#qe$3|^C
zpqAI;Bw1Xbq-Hm-<^?)N<74T8nk$X@hP6U|;BoIOe6CcNfjuTe1l<5hW#0N4C}cBp
zyy{O)AsdZ=etv3bC!qF)>I2#->$?ql@`PHZ)RRNdlZu3-rmmYC$n`g%7Qj{IzuUAh
zWPka=lnt;Ok@G9#{L&_|b8Ir7bCa#;P%%+`SqEt2fc{hp)rS;%eL*c#>gCP0!~7Li
z!_ACFmx_t%gVoTg$6Oy$+A*e<`I@Lj!$Z}&Hc2h#)e<G_dNxS~NXwdaM}-5jM8!i(
zATNDW%~h)H64bUCo>49r{PC-bB4cRR{RJ1asz=3A^+Bs#8;o#R%~fjEzuJ5sYG!>%
zg`@h)$8n*WRj>R~?35;FwO%UJ-jUT~PIS;4tv#1@5Wm{mQ?0K@nFCQ3<{#BVyHz-<
z@BfJ&npN9V>Y=~+N;Bi@m$xcXXPJ!ne<N*PUDxyfQtQk<6%*CB>2)ThmMKxt_cu#H
zn_e&V`zMJ9Z^TD5tI`JhmM@Zp6nfTj=X~WJD!X~V0S~&qu7BS6XL0F+Djd~Ufd^@K
z9o3uOb@amevr&(-YJE|2wmPcY_3LNidsdLJ;GguA`0&R4y%FEOAw&4evZQ<SeY+`%
zShFKiamu>OC5;Wn9u1<Ur!+TS-aLMq6<V{0lHq~wr0fjI0^EMw<QRIs@)#OeR5O;I
zQd|CNlWp;4y-{na44&<MUWTLn#S?-Rd(j)UmCA0GKmMur69Za2W&bAk6C2V#`<e`?
z+#ddAv*yc62I;YR$9d^Pc!vVtU10teY9b~~a%7iz2ZMhH&V0l5bS8eQmm!y=o<{|9
zCNmqFPA7wd>em^hQ@br#2u=EuiIRl_MvRDY)99dv8&3P;UC{`9Dn~kbH-ss7(FvgQ
z@6j1VhgdP&(02kIWRS$D$!tU4-=p(Wbe7OT<0ZKvKrEHgA*Q!5t{t5YaNsf(1E>6%
zPV`%uSTYUQcDqUea`<>V$(6hojWY}`52A%hqCW_J=0kLFL&5wFI^F2}M|6~f%?t|W
zOfNX_Js@IbMVW-@!w4(f%)>iSJ23`XuSlbfH4#r;Bd4jDevGqaZYN0;3X-A|zHyjN
z!F9X@8b$YD$^S~?$#B3SW`guyh=NaL3}f;PI$%(iE;TTy3uOKqIzK=M4Y16ALFeb_
zpjA|~!C`4~(9xEP-9!(Q$23&Giw-c*qIH?1TNv_VVnMf$i(k1JwRB~Ii9(wGOI@W8
zXWT-y{(lCoIJt%{eWzk&>uO_fp|SVF_*26N3BK{L(0G`qj|lV;j)tpP&Ik0}qq(D6
z+v*T8zU?UZcRcp}b@RV#{_)liw(|b{f`5N*_?fRw@O3{P<b68@-%dQhRXQdKrcSuN
z<yZ~uEChDKm%!$pM)|;?5J1D7&cv!Uw!-b+;N4epub|2Hm8Dns+D@Ui6AYN{TD%_i
z9OuRtuo3*7`5DfSmnmc$=ufuuo<oA?5a&5089=cN7<%11;Dvn4?z~m-Y+v>C7CgPY
zr%&+oah^VawLG35;r!iS1WG<^zM+}8tPMNvUVQyxc^jZf7nd&Hk1R)^K^l`ZY2D<b
z2<PsBS3#rb-}Z6G9&X<V^p_X!fWY;S(j5?Q{UX@D>z9w`_X)n|R(*X1U*8ieSYLg|
z1>f=95FDC^%PzGan(|J#dMDuRJFDx!Ghf{zd;j3_!MtJV*dlz7P5oBJEbl)q_>Xh)
z`-#8d;n>nx{@70rJe}nICj|cqPJTa8yZlK9@7XVS_H*)EbX8X#KV93wYJY6$81HKr
zeC<FmFiu~+^_md@Dsw-v3W5Erfun`M(Wf);1+KsuA#mnP2<^GMkz7Pi<}y^cR1p2(
z+G^<eLg@LA{CsFc2#xU0lY;XkC%@QK2|``t!^BeJerh?DJ6Y5PWN^w<=r08NpX`5n
zk`D|Cfpy0AG9r>Dg64?BP{p}cx&%S+JzfYM|HusO51kQ0XL#qR;2h=TR~;NJOyvlG
z+-3;|9~u@y!@LvDJdAMiix9dCuC1JF>&E@YJ9i7t-JEl`bQwcaWc{3mE2JULjqj$(
zzP3@Q?aJQ}YD45$Mukfta1R8pf<VPE4RaUun8K_%yy|N&_}cSBye}yDf@HeD9TWD=
z=N>)xZi;vB72JElvgtYiI6cJsT(5q3`zcWRalvz(li$tF0$oQ-Rd*D89r;P#_Z-aJ
zue0h3o&CqM7~Z!_@a_6~kgh~lUrO%M^!$kBL!&~d{6l$206hg)3+HMn<HYOWmT@2)
z^e^MS`l&7RpJRo<F}NDAnb|F)R)Ive7kt|vn|WWa;Oni(%q<FS0-EohdOQWoEA!K1
z{NXdgVdQ$c<w9LJXv$kFua*^AcWDsdC{1(A4}0F~c{B7VlsluJK>CKm^1ize*!|=T
zaLK@k5WvOiix&5at%fD|%&Nbq;O}`1i%q}a?+5!nobZNo;CNn&e%$u>!jls}t>zD$
z5)Pc&WDRgqtY@j`erP!aU!`)_EG7!>ZJc`>;WL4PCs6)5D)=ZB92(;q`U~EE&f8yX
z+A8<JqD>!=*FWg{{LnjRRy&RqI*vU37JT)qV@&A4E1R3>|Bw5JdC!R88R6ttbEEC!
z&;ai|AvjNP@=H!S1Pvoh0wld)Eq>S*<<Lk3?$qFy2Ik;4e*`YxM!o|dM<}J(B9Ul%
z8oqt&gfiq~uF}2%bz2N7hD0aI%kgRWLh@`n%G|{GH_#y>{EyH_b^}BRC-OH+!qUzH
zB~H;*`r_tvERl#rf(GeJNg#4k&yxdw3>r&`^CEaMFGI@j?|>*O{jC|z78=fWfm26$
zJJuWl3mk0IJ3|JGWeh&Pxq(~VWP!tYigTdK(z-@fa2hCU&6?3;39M1zd}e~+Yu}b4
zI!IE31upM{qa<O(0j~uPo+wV2!*UW(S?4y<mO+SL=lboIop4A{cIup1hvIA*Fj(L!
zqsAQp1Zxq&lGE{ukpSN851|p#+b|N3pu=IK_!$lWDhHShI;ipxZFoczKKx6*c2B;0
z51+Td4`stAfTb_;x#S@Eq%!%GvUK{CMj&{`M*7wd+9pMhE;F1ZCG;lN=%dL#jO;|+
zN&@U<lkf?_YJ{T3N*@?3eXv={i~m^!gLXM`2bJ7IWPBKeb3W457-^SUk3seLRTBpB
zOcgmwMNUvL%5GWO`nF*z+1x6dR<c_qyHo~ur(|0y?MKO0l<Y#4Z6~AbH`RS6*;q2T
zpOkixxLsua13EY(M8DE3^2KH8_A42(A5+pOjzYFS&_|@1eT?~^5X5PfSu;`;+;lV;
z*1C)aGwcVzp(#Vv9XtLNDd!#XSEQ=$kiR0em8<lALN#*wcg<)s7~vZGpUm-4hFu)B
zQ~MUFUQYiOsWwjk7O5Ui{}!ocPXDgaU5MuYcm8Gj;GBVK*peUQs&~M9$FT;VGc_P5
e-1uj)fotymOv><EeZCXblmSM*d;Lph#Qy?Vn$PV3

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/sftp_si.cpython-311.pyc b/paramiko/__pycache__/sftp_si.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8853e2a57420adaeafa8d7984395e6c9c9af05a4
GIT binary patch
literal 13863
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
M`~TSIuB_$10WQ^fHvj+t

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/ssh_exception.cpython-311.pyc b/paramiko/__pycache__/ssh_exception.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cd1c1583124e31ab8acf2d86f3a230f846e0209c
GIT binary patch
literal 10342
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
K8%<DbbN?HW52qCX

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/ssh_gss.cpython-311.pyc b/paramiko/__pycache__/ssh_gss.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ed1afa2cd65ad7768cada839d28c0d8346e4777d
GIT binary patch
literal 29820
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

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/transport.cpython-311.pyc b/paramiko/__pycache__/transport.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d64e8e4af7275502bbcfce408c18f417fc829368
GIT binary patch
literal 117148
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
BF--sf

literal 0
HcmV?d00001

diff --git a/paramiko/__pycache__/util.cpython-311.pyc b/paramiko/__pycache__/util.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..50fd1c067765f34a72f76633e01bd6581962ecd1
GIT binary patch
literal 14990
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

literal 0
HcmV?d00001

diff --git a/paramiko/_version.py b/paramiko/_version.py
new file mode 100644
index 0000000..ba3f1b3
--- /dev/null
+++ b/paramiko/_version.py
@@ -0,0 +1,2 @@
+__version_info__ = (3, 1, 0)
+__version__ = ".".join(map(str, __version_info__))
diff --git a/paramiko/_winapi.py b/paramiko/_winapi.py
new file mode 100644
index 0000000..4295457
--- /dev/null
+++ b/paramiko/_winapi.py
@@ -0,0 +1,413 @@
+"""
+Windows API functions implemented as ctypes functions and classes as found
+in jaraco.windows (3.4.1).
+
+If you encounter issues with this module, please consider reporting the issues
+in jaraco.windows and asking the author to port the fixes back here.
+"""
+
+import builtins
+import ctypes.wintypes
+
+from paramiko.util import u
+
+
+######################
+# jaraco.windows.error
+
+
+def format_system_message(errno):
+    """
+    Call FormatMessage with a system error number to retrieve
+    the descriptive error message.
+    """
+    # first some flags used by FormatMessageW
+    ALLOCATE_BUFFER = 0x100
+    FROM_SYSTEM = 0x1000
+
+    # Let FormatMessageW allocate the buffer (we'll free it below)
+    # Also, let it know we want a system error message.
+    flags = ALLOCATE_BUFFER | FROM_SYSTEM
+    source = None
+    message_id = errno
+    language_id = 0
+    result_buffer = ctypes.wintypes.LPWSTR()
+    buffer_size = 0
+    arguments = None
+    bytes = ctypes.windll.kernel32.FormatMessageW(
+        flags,
+        source,
+        message_id,
+        language_id,
+        ctypes.byref(result_buffer),
+        buffer_size,
+        arguments,
+    )
+    # note the following will cause an infinite loop if GetLastError
+    #  repeatedly returns an error that cannot be formatted, although
+    #  this should not happen.
+    handle_nonzero_success(bytes)
+    message = result_buffer.value
+    ctypes.windll.kernel32.LocalFree(result_buffer)
+    return message
+
+
+class WindowsError(builtins.WindowsError):
+    """more info about errors at
+    http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx"""
+
+    def __init__(self, value=None):
+        if value is None:
+            value = ctypes.windll.kernel32.GetLastError()
+        strerror = format_system_message(value)
+        args = 0, strerror, None, value
+        super().__init__(*args)
+
+    @property
+    def message(self):
+        return self.strerror
+
+    @property
+    def code(self):
+        return self.winerror
+
+    def __str__(self):
+        return self.message
+
+    def __repr__(self):
+        return "{self.__class__.__name__}({self.winerror})".format(**vars())
+
+
+def handle_nonzero_success(result):
+    if result == 0:
+        raise WindowsError()
+
+
+###########################
+# jaraco.windows.api.memory
+
+GMEM_MOVEABLE = 0x2
+
+GlobalAlloc = ctypes.windll.kernel32.GlobalAlloc
+GlobalAlloc.argtypes = ctypes.wintypes.UINT, ctypes.c_size_t
+GlobalAlloc.restype = ctypes.wintypes.HANDLE
+
+GlobalLock = ctypes.windll.kernel32.GlobalLock
+GlobalLock.argtypes = (ctypes.wintypes.HGLOBAL,)
+GlobalLock.restype = ctypes.wintypes.LPVOID
+
+GlobalUnlock = ctypes.windll.kernel32.GlobalUnlock
+GlobalUnlock.argtypes = (ctypes.wintypes.HGLOBAL,)
+GlobalUnlock.restype = ctypes.wintypes.BOOL
+
+GlobalSize = ctypes.windll.kernel32.GlobalSize
+GlobalSize.argtypes = (ctypes.wintypes.HGLOBAL,)
+GlobalSize.restype = ctypes.c_size_t
+
+CreateFileMapping = ctypes.windll.kernel32.CreateFileMappingW
+CreateFileMapping.argtypes = [
+    ctypes.wintypes.HANDLE,
+    ctypes.c_void_p,
+    ctypes.wintypes.DWORD,
+    ctypes.wintypes.DWORD,
+    ctypes.wintypes.DWORD,
+    ctypes.wintypes.LPWSTR,
+]
+CreateFileMapping.restype = ctypes.wintypes.HANDLE
+
+MapViewOfFile = ctypes.windll.kernel32.MapViewOfFile
+MapViewOfFile.restype = ctypes.wintypes.HANDLE
+
+UnmapViewOfFile = ctypes.windll.kernel32.UnmapViewOfFile
+UnmapViewOfFile.argtypes = (ctypes.wintypes.HANDLE,)
+
+RtlMoveMemory = ctypes.windll.kernel32.RtlMoveMemory
+RtlMoveMemory.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)
+
+ctypes.windll.kernel32.LocalFree.argtypes = (ctypes.wintypes.HLOCAL,)
+
+#####################
+# jaraco.windows.mmap
+
+
+class MemoryMap:
+    """
+    A memory map object which can have security attributes overridden.
+    """
+
+    def __init__(self, name, length, security_attributes=None):
+        self.name = name
+        self.length = length
+        self.security_attributes = security_attributes
+        self.pos = 0
+
+    def __enter__(self):
+        p_SA = (
+            ctypes.byref(self.security_attributes)
+            if self.security_attributes
+            else None
+        )
+        INVALID_HANDLE_VALUE = -1
+        PAGE_READWRITE = 0x4
+        FILE_MAP_WRITE = 0x2
+        filemap = ctypes.windll.kernel32.CreateFileMappingW(
+            INVALID_HANDLE_VALUE,
+            p_SA,
+            PAGE_READWRITE,
+            0,
+            self.length,
+            u(self.name),
+        )
+        handle_nonzero_success(filemap)
+        if filemap == INVALID_HANDLE_VALUE:
+            raise Exception("Failed to create file mapping")
+        self.filemap = filemap
+        self.view = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0)
+        return self
+
+    def seek(self, pos):
+        self.pos = pos
+
+    def write(self, msg):
+        assert isinstance(msg, bytes)
+        n = len(msg)
+        if self.pos + n >= self.length:  # A little safety.
+            raise ValueError(f"Refusing to write {n} bytes")
+        dest = self.view + self.pos
+        length = ctypes.c_size_t(n)
+        ctypes.windll.kernel32.RtlMoveMemory(dest, msg, length)
+        self.pos += n
+
+    def read(self, n):
+        """
+        Read n bytes from mapped view.
+        """
+        out = ctypes.create_string_buffer(n)
+        source = self.view + self.pos
+        length = ctypes.c_size_t(n)
+        ctypes.windll.kernel32.RtlMoveMemory(out, source, length)
+        self.pos += n
+        return out.raw
+
+    def __exit__(self, exc_type, exc_val, tb):
+        ctypes.windll.kernel32.UnmapViewOfFile(self.view)
+        ctypes.windll.kernel32.CloseHandle(self.filemap)
+
+
+#############################
+# jaraco.windows.api.security
+
+# from WinNT.h
+READ_CONTROL = 0x00020000
+STANDARD_RIGHTS_REQUIRED = 0x000F0000
+STANDARD_RIGHTS_READ = READ_CONTROL
+STANDARD_RIGHTS_WRITE = READ_CONTROL
+STANDARD_RIGHTS_EXECUTE = READ_CONTROL
+STANDARD_RIGHTS_ALL = 0x001F0000
+
+# from NTSecAPI.h
+POLICY_VIEW_LOCAL_INFORMATION = 0x00000001
+POLICY_VIEW_AUDIT_INFORMATION = 0x00000002
+POLICY_GET_PRIVATE_INFORMATION = 0x00000004
+POLICY_TRUST_ADMIN = 0x00000008
+POLICY_CREATE_ACCOUNT = 0x00000010
+POLICY_CREATE_SECRET = 0x00000020
+POLICY_CREATE_PRIVILEGE = 0x00000040
+POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080
+POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100
+POLICY_AUDIT_LOG_ADMIN = 0x00000200
+POLICY_SERVER_ADMIN = 0x00000400
+POLICY_LOOKUP_NAMES = 0x00000800
+POLICY_NOTIFICATION = 0x00001000
+
+POLICY_ALL_ACCESS = (
+    STANDARD_RIGHTS_REQUIRED
+    | POLICY_VIEW_LOCAL_INFORMATION
+    | POLICY_VIEW_AUDIT_INFORMATION
+    | POLICY_GET_PRIVATE_INFORMATION
+    | POLICY_TRUST_ADMIN
+    | POLICY_CREATE_ACCOUNT
+    | POLICY_CREATE_SECRET
+    | POLICY_CREATE_PRIVILEGE
+    | POLICY_SET_DEFAULT_QUOTA_LIMITS
+    | POLICY_SET_AUDIT_REQUIREMENTS
+    | POLICY_AUDIT_LOG_ADMIN
+    | POLICY_SERVER_ADMIN
+    | POLICY_LOOKUP_NAMES
+)
+
+
+POLICY_READ = (
+    STANDARD_RIGHTS_READ
+    | POLICY_VIEW_AUDIT_INFORMATION
+    | POLICY_GET_PRIVATE_INFORMATION
+)
+
+POLICY_WRITE = (
+    STANDARD_RIGHTS_WRITE
+    | POLICY_TRUST_ADMIN
+    | POLICY_CREATE_ACCOUNT
+    | POLICY_CREATE_SECRET
+    | POLICY_CREATE_PRIVILEGE
+    | POLICY_SET_DEFAULT_QUOTA_LIMITS
+    | POLICY_SET_AUDIT_REQUIREMENTS
+    | POLICY_AUDIT_LOG_ADMIN
+    | POLICY_SERVER_ADMIN
+)
+
+POLICY_EXECUTE = (
+    STANDARD_RIGHTS_EXECUTE
+    | POLICY_VIEW_LOCAL_INFORMATION
+    | POLICY_LOOKUP_NAMES
+)
+
+
+class TokenAccess:
+    TOKEN_QUERY = 0x8
+
+
+class TokenInformationClass:
+    TokenUser = 1
+
+
+class TOKEN_USER(ctypes.Structure):
+    num = 1
+    _fields_ = [
+        ("SID", ctypes.c_void_p),
+        ("ATTRIBUTES", ctypes.wintypes.DWORD),
+    ]
+
+
+class SECURITY_DESCRIPTOR(ctypes.Structure):
+    """
+    typedef struct _SECURITY_DESCRIPTOR
+        {
+        UCHAR Revision;
+        UCHAR Sbz1;
+        SECURITY_DESCRIPTOR_CONTROL Control;
+        PSID Owner;
+        PSID Group;
+        PACL Sacl;
+        PACL Dacl;
+        }   SECURITY_DESCRIPTOR;
+    """
+
+    SECURITY_DESCRIPTOR_CONTROL = ctypes.wintypes.USHORT
+    REVISION = 1
+
+    _fields_ = [
+        ("Revision", ctypes.c_ubyte),
+        ("Sbz1", ctypes.c_ubyte),
+        ("Control", SECURITY_DESCRIPTOR_CONTROL),
+        ("Owner", ctypes.c_void_p),
+        ("Group", ctypes.c_void_p),
+        ("Sacl", ctypes.c_void_p),
+        ("Dacl", ctypes.c_void_p),
+    ]
+
+
+class SECURITY_ATTRIBUTES(ctypes.Structure):
+    """
+    typedef struct _SECURITY_ATTRIBUTES {
+        DWORD  nLength;
+        LPVOID lpSecurityDescriptor;
+        BOOL   bInheritHandle;
+    } SECURITY_ATTRIBUTES;
+    """
+
+    _fields_ = [
+        ("nLength", ctypes.wintypes.DWORD),
+        ("lpSecurityDescriptor", ctypes.c_void_p),
+        ("bInheritHandle", ctypes.wintypes.BOOL),
+    ]
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES)
+
+    @property
+    def descriptor(self):
+        return self._descriptor
+
+    @descriptor.setter
+    def descriptor(self, value):
+        self._descriptor = value
+        self.lpSecurityDescriptor = ctypes.addressof(value)
+
+
+ctypes.windll.advapi32.SetSecurityDescriptorOwner.argtypes = (
+    ctypes.POINTER(SECURITY_DESCRIPTOR),
+    ctypes.c_void_p,
+    ctypes.wintypes.BOOL,
+)
+
+#########################
+# jaraco.windows.security
+
+
+def GetTokenInformation(token, information_class):
+    """
+    Given a token, get the token information for it.
+    """
+    data_size = ctypes.wintypes.DWORD()
+    ctypes.windll.advapi32.GetTokenInformation(
+        token, information_class.num, 0, 0, ctypes.byref(data_size)
+    )
+    data = ctypes.create_string_buffer(data_size.value)
+    handle_nonzero_success(
+        ctypes.windll.advapi32.GetTokenInformation(
+            token,
+            information_class.num,
+            ctypes.byref(data),
+            ctypes.sizeof(data),
+            ctypes.byref(data_size),
+        )
+    )
+    return ctypes.cast(data, ctypes.POINTER(TOKEN_USER)).contents
+
+
+def OpenProcessToken(proc_handle, access):
+    result = ctypes.wintypes.HANDLE()
+    proc_handle = ctypes.wintypes.HANDLE(proc_handle)
+    handle_nonzero_success(
+        ctypes.windll.advapi32.OpenProcessToken(
+            proc_handle, access, ctypes.byref(result)
+        )
+    )
+    return result
+
+
+def get_current_user():
+    """
+    Return a TOKEN_USER for the owner of this process.
+    """
+    process = OpenProcessToken(
+        ctypes.windll.kernel32.GetCurrentProcess(), TokenAccess.TOKEN_QUERY
+    )
+    return GetTokenInformation(process, TOKEN_USER)
+
+
+def get_security_attributes_for_user(user=None):
+    """
+    Return a SECURITY_ATTRIBUTES structure with the SID set to the
+    specified user (uses current user if none is specified).
+    """
+    if user is None:
+        user = get_current_user()
+
+    assert isinstance(user, TOKEN_USER), "user must be TOKEN_USER instance"
+
+    SD = SECURITY_DESCRIPTOR()
+    SA = SECURITY_ATTRIBUTES()
+    # by attaching the actual security descriptor, it will be garbage-
+    # collected with the security attributes
+    SA.descriptor = SD
+    SA.bInheritHandle = 1
+
+    ctypes.windll.advapi32.InitializeSecurityDescriptor(
+        ctypes.byref(SD), SECURITY_DESCRIPTOR.REVISION
+    )
+    ctypes.windll.advapi32.SetSecurityDescriptorOwner(
+        ctypes.byref(SD), user.SID, 0
+    )
+    return SA
diff --git a/paramiko/agent.py b/paramiko/agent.py
new file mode 100644
index 0000000..30ec159
--- /dev/null
+++ b/paramiko/agent.py
@@ -0,0 +1,450 @@
+# Copyright (C) 2003-2007  John Rochester <john@jrochester.org>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+SSH Agent interface
+"""
+
+import os
+import socket
+import struct
+import sys
+import threading
+import time
+import tempfile
+import stat
+from select import select
+from paramiko.common import io_sleep, byte_chr
+
+from paramiko.ssh_exception import SSHException, AuthenticationException
+from paramiko.message import Message
+from paramiko.pkey import PKey
+from paramiko.util import asbytes
+
+cSSH2_AGENTC_REQUEST_IDENTITIES = byte_chr(11)
+SSH2_AGENT_IDENTITIES_ANSWER = 12
+cSSH2_AGENTC_SIGN_REQUEST = byte_chr(13)
+SSH2_AGENT_SIGN_RESPONSE = 14
+
+SSH_AGENT_RSA_SHA2_256 = 2
+SSH_AGENT_RSA_SHA2_512 = 4
+# NOTE: RFC mildly confusing; while these flags are OR'd together, OpenSSH at
+# least really treats them like "AND"s, in the sense that if it finds the
+# SHA256 flag set it won't continue looking at the SHA512 one; it
+# short-circuits right away.
+# Thus, we never want to eg submit 6 to say "either's good".
+ALGORITHM_FLAG_MAP = {
+    "rsa-sha2-256": SSH_AGENT_RSA_SHA2_256,
+    "rsa-sha2-512": SSH_AGENT_RSA_SHA2_512,
+}
+
+
+class AgentSSH:
+    def __init__(self):
+        self._conn = None
+        self._keys = ()
+
+    def get_keys(self):
+        """
+        Return the list of keys available through the SSH agent, if any.  If
+        no SSH agent was running (or it couldn't be contacted), an empty list
+        will be returned.
+
+        This method performs no IO, just returns the list of keys retrieved
+        when the connection was made.
+
+        :return:
+            a tuple of `.AgentKey` objects representing keys available on the
+            SSH agent
+        """
+        return self._keys
+
+    def _connect(self, conn):
+        self._conn = conn
+        ptype, result = self._send_message(cSSH2_AGENTC_REQUEST_IDENTITIES)
+        if ptype != SSH2_AGENT_IDENTITIES_ANSWER:
+            raise SSHException("could not get keys from ssh-agent")
+        keys = []
+        for i in range(result.get_int()):
+            keys.append(AgentKey(self, result.get_binary()))
+            result.get_string()
+        self._keys = tuple(keys)
+
+    def _close(self):
+        if self._conn is not None:
+            self._conn.close()
+        self._conn = None
+        self._keys = ()
+
+    def _send_message(self, msg):
+        msg = asbytes(msg)
+        self._conn.send(struct.pack(">I", len(msg)) + msg)
+        data = self._read_all(4)
+        msg = Message(self._read_all(struct.unpack(">I", data)[0]))
+        return ord(msg.get_byte()), msg
+
+    def _read_all(self, wanted):
+        result = self._conn.recv(wanted)
+        while len(result) < wanted:
+            if len(result) == 0:
+                raise SSHException("lost ssh-agent")
+            extra = self._conn.recv(wanted - len(result))
+            if len(extra) == 0:
+                raise SSHException("lost ssh-agent")
+            result += extra
+        return result
+
+
+class AgentProxyThread(threading.Thread):
+    """
+    Class in charge of communication between two channels.
+    """
+
+    def __init__(self, agent):
+        threading.Thread.__init__(self, target=self.run)
+        self._agent = agent
+        self._exit = False
+
+    def run(self):
+        try:
+            (r, addr) = self.get_connection()
+            # Found that r should be either
+            # a socket from the socket library or None
+            self.__inr = r
+            # The address should be an IP address as a string? or None
+            self.__addr = addr
+            self._agent.connect()
+            if not isinstance(self._agent, int) and (
+                self._agent._conn is None
+                or not hasattr(self._agent._conn, "fileno")
+            ):
+                raise AuthenticationException("Unable to connect to SSH agent")
+            self._communicate()
+        except:
+            # XXX Not sure what to do here ... raise or pass ?
+            raise
+
+    def _communicate(self):
+        import fcntl
+
+        oldflags = fcntl.fcntl(self.__inr, fcntl.F_GETFL)
+        fcntl.fcntl(self.__inr, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
+        while not self._exit:
+            events = select([self._agent._conn, self.__inr], [], [], 0.5)
+            for fd in events[0]:
+                if self._agent._conn == fd:
+                    data = self._agent._conn.recv(512)
+                    if len(data) != 0:
+                        self.__inr.send(data)
+                    else:
+                        self._close()
+                        break
+                elif self.__inr == fd:
+                    data = self.__inr.recv(512)
+                    if len(data) != 0:
+                        self._agent._conn.send(data)
+                    else:
+                        self._close()
+                        break
+            time.sleep(io_sleep)
+
+    def _close(self):
+        self._exit = True
+        self.__inr.close()
+        self._agent._conn.close()
+
+
+class AgentLocalProxy(AgentProxyThread):
+    """
+    Class to be used when wanting to ask a local SSH Agent being
+    asked from a remote fake agent (so use a unix socket for ex.)
+    """
+
+    def __init__(self, agent):
+        AgentProxyThread.__init__(self, agent)
+
+    def get_connection(self):
+        """
+        Return a pair of socket object and string address.
+
+        May block!
+        """
+        conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        try:
+            conn.bind(self._agent._get_filename())
+            conn.listen(1)
+            (r, addr) = conn.accept()
+            return r, addr
+        except:
+            raise
+
+
+class AgentRemoteProxy(AgentProxyThread):
+    """
+    Class to be used when wanting to ask a remote SSH Agent
+    """
+
+    def __init__(self, agent, chan):
+        AgentProxyThread.__init__(self, agent)
+        self.__chan = chan
+
+    def get_connection(self):
+        return self.__chan, None
+
+
+def get_agent_connection():
+    """
+    Returns some SSH agent object, or None if none were found/supported.
+
+    .. versionadded:: 2.10
+    """
+    if ("SSH_AUTH_SOCK" in os.environ) and (sys.platform != "win32"):
+        conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        try:
+            conn.connect(os.environ["SSH_AUTH_SOCK"])
+            return conn
+        except:
+            # probably a dangling env var: the ssh agent is gone
+            return
+    elif sys.platform == "win32":
+        from . import win_pageant, win_openssh
+
+        conn = None
+        if win_pageant.can_talk_to_agent():
+            conn = win_pageant.PageantConnection()
+        elif win_openssh.can_talk_to_agent():
+            conn = win_openssh.OpenSSHAgentConnection()
+        return conn
+    else:
+        # no agent support
+        return
+
+
+class AgentClientProxy:
+    """
+    Class proxying request as a client:
+
+    #. client ask for a request_forward_agent()
+    #. server creates a proxy and a fake SSH Agent
+    #. server ask for establishing a connection when needed,
+       calling the forward_agent_handler at client side.
+    #. the forward_agent_handler launch a thread for connecting
+       the remote fake agent and the local agent
+    #. Communication occurs ...
+    """
+
+    def __init__(self, chanRemote):
+        self._conn = None
+        self.__chanR = chanRemote
+        self.thread = AgentRemoteProxy(self, chanRemote)
+        self.thread.start()
+
+    def __del__(self):
+        self.close()
+
+    def connect(self):
+        """
+        Method automatically called by ``AgentProxyThread.run``.
+        """
+        conn = get_agent_connection()
+        if not conn:
+            return
+        self._conn = conn
+
+    def close(self):
+        """
+        Close the current connection and terminate the agent
+        Should be called manually
+        """
+        if hasattr(self, "thread"):
+            self.thread._exit = True
+            self.thread.join(1000)
+        if self._conn is not None:
+            self._conn.close()
+
+
+class AgentServerProxy(AgentSSH):
+    """
+    Allows an SSH server to access a forwarded agent.
+
+    This also creates a unix domain socket on the system to allow external
+    programs to also access the agent. For this reason, you probably only want
+    to create one of these.
+
+    :meth:`connect` must be called before it is usable. This will also load the
+    list of keys the agent contains. You must also call :meth:`close` in
+    order to clean up the unix socket and the thread that maintains it.
+    (:class:`contextlib.closing` might be helpful to you.)
+
+    :param .Transport t: Transport used for SSH Agent communication forwarding
+
+    :raises: `.SSHException` -- mostly if we lost the agent
+    """
+
+    def __init__(self, t):
+        AgentSSH.__init__(self)
+        self.__t = t
+        self._dir = tempfile.mkdtemp("sshproxy")
+        os.chmod(self._dir, stat.S_IRWXU)
+        self._file = self._dir + "/sshproxy.ssh"
+        self.thread = AgentLocalProxy(self)
+        self.thread.start()
+
+    def __del__(self):
+        self.close()
+
+    def connect(self):
+        conn_sock = self.__t.open_forward_agent_channel()
+        if conn_sock is None:
+            raise SSHException("lost ssh-agent")
+        conn_sock.set_name("auth-agent")
+        self._connect(conn_sock)
+
+    def close(self):
+        """
+        Terminate the agent, clean the files, close connections
+        Should be called manually
+        """
+        os.remove(self._file)
+        os.rmdir(self._dir)
+        self.thread._exit = True
+        self.thread.join(1000)
+        self._close()
+
+    def get_env(self):
+        """
+        Helper for the environment under unix
+
+        :return:
+            a dict containing the ``SSH_AUTH_SOCK`` environment variables
+        """
+        return {"SSH_AUTH_SOCK": self._get_filename()}
+
+    def _get_filename(self):
+        return self._file
+
+
+class AgentRequestHandler:
+    """
+    Primary/default implementation of SSH agent forwarding functionality.
+
+    Simply instantiate this class, handing it a live command-executing session
+    object, and it will handle forwarding any local SSH agent processes it
+    finds.
+
+    For example::
+
+        # Connect
+        client = SSHClient()
+        client.connect(host, port, username)
+        # Obtain session
+        session = client.get_transport().open_session()
+        # Forward local agent
+        AgentRequestHandler(session)
+        # Commands executed after this point will see the forwarded agent on
+        # the remote end.
+        session.exec_command("git clone https://my.git.repository/")
+    """
+
+    def __init__(self, chanClient):
+        self._conn = None
+        self.__chanC = chanClient
+        chanClient.request_forward_agent(self._forward_agent_handler)
+        self.__clientProxys = []
+
+    def _forward_agent_handler(self, chanRemote):
+        self.__clientProxys.append(AgentClientProxy(chanRemote))
+
+    def __del__(self):
+        self.close()
+
+    def close(self):
+        for p in self.__clientProxys:
+            p.close()
+
+
+class Agent(AgentSSH):
+    """
+    Client interface for using private keys from an SSH agent running on the
+    local machine.  If an SSH agent is running, this class can be used to
+    connect to it and retrieve `.PKey` objects which can be used when
+    attempting to authenticate to remote SSH servers.
+
+    Upon initialization, a session with the local machine's SSH agent is
+    opened, if one is running. If no agent is running, initialization will
+    succeed, but `get_keys` will return an empty tuple.
+
+    :raises: `.SSHException` --
+        if an SSH agent is found, but speaks an incompatible protocol
+
+    .. versionchanged:: 2.10
+        Added support for native openssh agent on windows (extending previous
+        putty pageant support)
+    """
+
+    def __init__(self):
+        AgentSSH.__init__(self)
+
+        conn = get_agent_connection()
+        if not conn:
+            return
+        self._connect(conn)
+
+    def close(self):
+        """
+        Close the SSH agent connection.
+        """
+        self._close()
+
+
+class AgentKey(PKey):
+    """
+    Private key held in a local SSH agent.  This type of key can be used for
+    authenticating to a remote server (signing).  Most other key operations
+    work as expected.
+    """
+
+    def __init__(self, agent, blob):
+        self.agent = agent
+        self.blob = blob
+        self.public_blob = None
+        self.name = Message(blob).get_text()
+
+    def asbytes(self):
+        return self.blob
+
+    def __str__(self):
+        return self.asbytes()
+
+    def get_name(self):
+        return self.name
+
+    @property
+    def _fields(self):
+        raise NotImplementedError
+
+    def sign_ssh_data(self, data, algorithm=None):
+        msg = Message()
+        msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
+        msg.add_string(self.blob)
+        msg.add_string(data)
+        msg.add_int(ALGORITHM_FLAG_MAP.get(algorithm, 0))
+        ptype, result = self.agent._send_message(msg)
+        if ptype != SSH2_AGENT_SIGN_RESPONSE:
+            raise SSHException("key cannot be used for signing")
+        return result.get_binary()
diff --git a/paramiko/auth_handler.py b/paramiko/auth_handler.py
new file mode 100644
index 0000000..8c626b4
--- /dev/null
+++ b/paramiko/auth_handler.py
@@ -0,0 +1,1056 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+`.AuthHandler`
+"""
+
+import weakref
+import time
+import socket
+import re
+import json
+import base64
+
+from paramiko.common import (
+    cMSG_SERVICE_REQUEST,
+    cMSG_DISCONNECT,
+    DISCONNECT_SERVICE_NOT_AVAILABLE,
+    DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+    cMSG_USERAUTH_REQUEST,
+    cMSG_SERVICE_ACCEPT,
+    DEBUG,
+    AUTH_SUCCESSFUL,
+    INFO,
+    cMSG_USERAUTH_SUCCESS,
+    cMSG_USERAUTH_FAILURE,
+    AUTH_PARTIALLY_SUCCESSFUL,
+    cMSG_USERAUTH_INFO_REQUEST,
+    WARNING,
+    AUTH_FAILED,
+    cMSG_USERAUTH_PK_OK,
+    cMSG_USERAUTH_INFO_RESPONSE,
+    MSG_SERVICE_REQUEST,
+    MSG_SERVICE_ACCEPT,
+    MSG_USERAUTH_REQUEST,
+    MSG_USERAUTH_SUCCESS,
+    MSG_USERAUTH_FAILURE,
+    MSG_USERAUTH_BANNER,
+    MSG_USERAUTH_INFO_REQUEST,
+    MSG_USERAUTH_INFO_RESPONSE,
+    cMSG_USERAUTH_GSSAPI_RESPONSE,
+    cMSG_USERAUTH_GSSAPI_TOKEN,
+    cMSG_USERAUTH_GSSAPI_MIC,
+    MSG_USERAUTH_GSSAPI_RESPONSE,
+    MSG_USERAUTH_GSSAPI_TOKEN,
+    MSG_USERAUTH_GSSAPI_ERROR,
+    MSG_USERAUTH_GSSAPI_ERRTOK,
+    MSG_USERAUTH_GSSAPI_MIC,
+    MSG_NAMES,
+    cMSG_USERAUTH_BANNER,
+)
+from paramiko.message import Message
+from paramiko.util import b, u
+from paramiko.ssh_exception import (
+    SSHException,
+    AuthenticationException,
+    BadAuthenticationType,
+    PartialAuthentication,
+)
+from paramiko.server import InteractiveQuery
+from paramiko.ssh_gss import GSSAuth, GSS_EXCEPTIONS
+
+
+class AuthHandler:
+    """
+    Internal class to handle the mechanics of authentication.
+    """
+
+    def __init__(self, transport):
+        self.transport = weakref.proxy(transport)
+        self.username = None
+        self.authenticated = False
+        self.auth_event = None
+        self.auth_method = ""
+        self.banner = None
+        self.password = None
+        self.private_key = None
+        self.interactive_handler = None
+        self.submethods = None
+        # for server mode:
+        self.auth_username = None
+        self.auth_fail_count = 0
+        # for GSSAPI
+        self.gss_host = None
+        self.gss_deleg_creds = True
+
+    def _log(self, *args):
+        print("_log")
+        return self.transport._log(*args)
+
+    def is_authenticated(self):
+        print("is_authenticated")
+        return self.authenticated
+
+    def get_username(self):
+        print("get_username")
+        if self.transport.server_mode:
+            return self.auth_username
+        else:
+            return self.username
+
+    def auth_none(self, username, event):
+        print("auth_none")
+        self.transport.lock.acquire()
+        try:
+            self.auth_event = event
+            self.auth_method = "none"
+            self.username = username
+            self._request_auth()
+        finally:
+            self.transport.lock.release()
+
+    def auth_publickey(self, username, key, event):
+        print("auth_publickey")
+        self.transport.lock.acquire()
+        try:
+            self.auth_event = event
+            self.auth_method = "publickey"
+            self.username = username
+            self.private_key = key
+            print(self.private_key)
+            self._request_auth()
+        finally:
+            self.transport.lock.release()
+
+    def auth_password(self, username, password, event):
+        print("auth_password")
+        self.transport.lock.acquire()
+        try:
+            self.auth_event = event
+            self.auth_method = "password"
+            self.username = username
+            self.password = password
+            self._request_auth()
+        finally:
+            self.transport.lock.release()
+
+    def auth_interactive(self, username, handler, event, submethods=""):
+        print("auth_interactive")
+        """
+        response_list = handler(title, instructions, prompt_list)
+        """
+        self.transport.lock.acquire()
+        try:
+            self.auth_event = event
+            self.auth_method = "keyboard-interactive"
+            self.username = username
+            self.interactive_handler = handler
+            self.submethods = submethods
+            self._request_auth()
+        finally:
+            self.transport.lock.release()
+
+    def auth_gssapi_with_mic(self, username, gss_host, gss_deleg_creds, event):
+        print("auth_gssapi_with_mic")
+        self.transport.lock.acquire()
+        try:
+            self.auth_event = event
+            self.auth_method = "gssapi-with-mic"
+            self.username = username
+            self.gss_host = gss_host
+            self.gss_deleg_creds = gss_deleg_creds
+            self._request_auth()
+        finally:
+            self.transport.lock.release()
+
+    def auth_gssapi_keyex(self, username, event):
+        print("auth_gssapi_keyex")
+        self.transport.lock.acquire()
+        try:
+            self.auth_event = event
+            self.auth_method = "gssapi-keyex"
+            self.username = username
+            self._request_auth()
+        finally:
+            self.transport.lock.release()
+
+    def abort(self):
+        print("abort")
+        if self.auth_event is not None:
+            self.auth_event.set()
+
+    # ...internals...
+
+    def _request_auth(self):
+        print("_request_auth")
+        m = Message()
+        m.add_byte(cMSG_SERVICE_REQUEST)
+        m.add_string("ssh-userauth")
+
+        print(m)
+        self.transport._send_message(m)
+
+    def _disconnect_service_not_available(self):
+        print("_disconnect_service_not_available")
+        m = Message()
+        m.add_byte(cMSG_DISCONNECT)
+        m.add_int(DISCONNECT_SERVICE_NOT_AVAILABLE)
+        m.add_string("Service not available")
+        m.add_string("en")
+        self.transport._send_message(m)
+        self.transport.close()
+
+    def _disconnect_no_more_auth(self):
+        print("_disconnect_no_more_auth")
+        m = Message()
+        m.add_byte(cMSG_DISCONNECT)
+        m.add
+
+
+    def _get_key_type_and_bits(self, key):
+        print("_get_key_type_and_bits")
+        """
+        Given any key, return its type/algorithm & bits-to-sign.
+
+        Intended for input to or verification of, key signatures.
+        """
+        # Use certificate contents, if available, plain pubkey otherwise
+        if key.public_blob:
+            return key.public_blob.key_type, key.public_blob.key_blob
+        else:
+            return key.get_name(), key
+
+    # LE CHALLENGE *********************************************
+    def _get_session_blob(self, key, service, username, algorithm):
+        print("_get_session_blob ********************************")
+        m = Message()
+        m.add_string(self.transport.session_id)
+        # problématique car comment avoir la session_id avant de s'authentifier ?
+        print("session_id: ", self.transport.session_id)
+        m.add_byte(cMSG_USERAUTH_REQUEST)
+        print("cMSG_USERAUTH_REQUEST", cMSG_USERAUTH_REQUEST)
+        m.add_string(username)
+        print("username: ", username)
+        m.add_string(service)
+        print("service: ", service)
+        m.add_string("publickey")
+        m.add_boolean(True)
+        _, bits = self._get_key_type_and_bits(key)
+        m.add_string(algorithm)
+        m.add_string(bits)
+        print("*****************************")
+        return m.asbytes()
+
+    def wait_for_response(self, event):
+        print("wait_for_response")
+        max_ts = None
+        if self.transport.auth_timeout is not None:
+            max_ts = time.time() + self.transport.auth_timeout
+        while True:
+            event.wait(0.1)
+            if not self.transport.is_active():
+                e = self.transport.get_exception()
+                if (e is None) or issubclass(e.__class__, EOFError):
+                    e = AuthenticationException("Authentication failed.")
+                raise e
+            if event.is_set():
+                break
+            if max_ts is not None and max_ts <= time.time():
+                raise AuthenticationException("Authentication timeout.")
+
+        if not self.is_authenticated():
+            e = self.transport.get_exception()
+            if e is None:
+                e = AuthenticationException("Authentication failed.")
+            # this is horrible.  Python Exception isn't yet descended from
+            # object, so type(e) won't work. :(
+            if issubclass(e.__class__, PartialAuthentication):
+                return e.allowed_types
+            raise e
+        return []
+
+    def _parse_service_request(self, m):
+        print("_parse_service_request")
+        service = m.get_text()
+        if self.transport.server_mode and (service == "ssh-userauth"):
+            # accepted
+            m = Message()
+            m.add_byte(cMSG_SERVICE_ACCEPT)
+            m.add_string(service)
+            self.transport._send_message(m)
+            banner, language = self.transport.server_object.get_banner()
+            if banner:
+                m = Message()
+                m.add_byte(cMSG_USERAUTH_BANNER)
+                m.add_string(banner)
+                m.add_string(language)
+                self.transport._send_message(m)
+            return
+        # dunno this one
+        self._disconnect_service_not_available()
+
+    def _generate_key_from_request(self, algorithm, keyblob):
+        print("_generate_key_from_request")
+        # For use in server mode.
+        options = self.transport.preferred_pubkeys
+        if algorithm.replace("-cert-v01@openssh.com", "") not in options:
+            err = (
+                "Auth rejected: pubkey algorithm '{}' unsupported or disabled"
+            )
+            self._log(INFO, err.format(algorithm))
+            return None
+        return self.transport._key_info[algorithm](Message(keyblob))
+
+    def _finalize_pubkey_algorithm(self, key_type):
+        print("_finalize_pubkey_algorithm")
+        # Short-circuit for non-RSA keys
+        if "rsa" not in key_type:
+            return key_type
+        self._log(
+            DEBUG,
+            "Finalizing pubkey algorithm for key of type {!r}".format(
+                key_type
+            ),
+        )
+        # NOTE re #2017: When the key is an RSA cert and the remote server is
+        # OpenSSH 7.7 or earlier, always use ssh-rsa-cert-v01@openssh.com.
+        # Those versions of the server won't support rsa-sha2 family sig algos
+        # for certs specifically, and in tandem with various server bugs
+        # regarding server-sig-algs, it's impossible to fit this into the rest
+        # of the logic here.
+        if key_type.endswith("-cert-v01@openssh.com") and re.search(
+            r"-OpenSSH_(?:[1-6]|7\.[0-7])", self.transport.remote_version
+        ):
+            pubkey_algo = "ssh-rsa-cert-v01@openssh.com"
+            self.transport._agreed_pubkey_algorithm = pubkey_algo
+            self._log(DEBUG, "OpenSSH<7.8 + RSA cert = forcing ssh-rsa!")
+            self._log(
+                DEBUG, "Agreed upon {!r} pubkey algorithm".format(pubkey_algo)
+            )
+            return pubkey_algo
+        # Normal attempts to handshake follow from here.
+        # Only consider RSA algos from our list, lest we agree on another!
+        my_algos = [x for x in self.transport.preferred_pubkeys if "rsa" in x]
+        self._log(DEBUG, "Our pubkey algorithm list: {}".format(my_algos))
+        # Short-circuit negatively if user disabled all RSA algos (heh)
+        if not my_algos:
+            raise SSHException(
+                "An RSA key was specified, but no RSA pubkey algorithms are configured!"  # noqa
+            )
+        # Check for server-sig-algs if supported & sent
+        server_algo_str = u(
+            self.transport.server_extensions.get("server-sig-algs", b(""))
+        )
+        pubkey_algo = None
+        if server_algo_str:
+            server_algos = server_algo_str.split(",")
+            self._log(
+                DEBUG, "Server-side algorithm list: {}".format(server_algos)
+            )
+            # Only use algos from our list that the server likes, in our own
+            # preference order. (NOTE: purposefully using same style as in
+            # Transport...expect to refactor later)
+            agreement = list(filter(server_algos.__contains__, my_algos))
+            if agreement:
+                pubkey_algo = agreement[0]
+                self._log(
+                    DEBUG,
+                    "Agreed upon {!r} pubkey algorithm".format(pubkey_algo),
+                )
+            else:
+                self._log(DEBUG, "No common pubkey algorithms exist! Dying.")
+                # TODO: MAY want to use IncompatiblePeer again here but that's
+                # technically for initial key exchange, not pubkey auth.
+                err = "Unable to agree on a pubkey algorithm for signing a {!r} key!"  # noqa
+                raise AuthenticationException(err.format(key_type))
+        else:
+            # Fallback: first one in our (possibly tweaked by caller) list
+            pubkey_algo = my_algos[0]
+            msg = "Server did not send a server-sig-algs list; defaulting to our first preferred algo ({!r})"  # noqa
+            self._log(DEBUG, msg.format(pubkey_algo))
+            self._log(
+                DEBUG,
+                "NOTE: you may use the 'disabled_algorithms' SSHClient/Transport init kwarg to disable that or other algorithms if your server does not support them!",  # noqa
+            )
+        if key_type.endswith("-cert-v01@openssh.com"):
+            pubkey_algo += "-cert-v01@openssh.com"
+        self.transport._agreed_pubkey_algorithm = pubkey_algo
+        return pubkey_algo
+
+    def _parse_service_accept(self, m):
+        print("_parse_service_accept")
+        service = m.get_text()
+        print("teeest")
+        if service == "ssh-userauth":
+            # TODO 4.0: this message sucks ass. change it to something more
+            # obvious. it always appears to mean "we already authed" but no! it
+            # just means "we are allowed to TRY authing!"
+            self._log(DEBUG, "userauth is OK")
+            m = Message()
+            mes2 = Message()
+            m.add_byte(cMSG_USERAUTH_REQUEST)
+            m.add_string(self.username)
+            
+            m.add_string("ssh-connection")
+            m.add_string(self.auth_method)
+            if self.auth_method == "password":
+                m.add_boolean(False)
+                password = b(self.password)
+                m.add_string(password)
+            elif self.auth_method == "publickey":
+                print("en mode public key :)")
+
+
+                # m.add_boolean(True)
+                # TODO : aller chercher la clé privée autre part ??
+
+                info_dict = {
+                    "algorithm": "rsa-sha2-512",
+                    "session_id": base64.b64encode(self.transport.session_id).decode(), # decode to convert bytes to string
+                    "userauth_request": base64.b64encode(cMSG_USERAUTH_REQUEST).decode(),
+                    "username": self.username,
+                    # "public_key" : self.private_key.get_base64(), # here it's a public key!!
+                }
+
+
+                signer_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                signer_hostname = "localhost"
+                signer_port = 12340
+                signer_client.connect((signer_hostname, signer_port))
+                signer_client.send(json.dumps(info_dict).encode())
+
+                # faire dans auth :
+                # key_type, bits = self._get_key_type_and_bits(self.private_key)
+                # print("key type : ", key_type)
+                # print("bits : ", bits)
+
+                # hard code pour l'instant
+                # TODO : faire en sorte que ça marche avec d'autres clés
+                # algorithm = self._finalize_pubkey_algorithm(key_type)
+                # print("algorithm : ", algorithm)
+                
+                # faire dans auth :
+                # m.add_string("rsa-sha2-512")
+                # m.add_string(bits)
+
+                # TODO : envoyer le session ID au serveur !!
+                # print("session ID : ", self.transport.session_id)
+                # TODO : envoyer le cMSG_USERAUTH_REQUEST au serveur !!
+                # print("cMSG_USERAUTH_REQUEST : ", cMSG_USERAUTH_REQUEST)
+                
+                # faire dans auth :
+                # blob = self._get_session_blob(
+                #     self.private_key,
+                #     "ssh-connection",
+                #     self.username,
+                #     algorithm,
+                # )
+
+
+                # print("session blob : ", blob)
+                # faire dans auth :
+                # sig = self.private_key.sign_ssh_data(blob, algorithm)
+                # print("signature : ", sig)
+
+                
+                # TODO : recupérer le message du serveur
+                
+                data = signer_client.recv(2048)
+    # 
+                # Reconstruct the Paramiko Message object
+                mess = Message(data)
+                # print("data : ", data)
+                # info_dict_retour = json.loads(data.decode())
+
+                # print("signature : ", Message(sig))
+                # sig = self.private_key.sign_ssh_data(blob, algorithm)
+                
+                m = mess
+                
+
+                # m.add_string()
+            elif self.auth_method == "keyboard-interactive":
+                m.add_string("")
+                m.add_string(self.submethods)
+            elif self.auth_method == "gssapi-with-mic":
+                sshgss = GSSAuth(self.auth_method, self.gss_deleg_creds)
+                m.add_bytes(sshgss.ssh_gss_oids())
+                # send the supported GSSAPI OIDs to the server
+                self.transport._send_message(m)
+                ptype, m = self.transport.packetizer.read_message()
+                if ptype == MSG_USERAUTH_BANNER:
+                    self._parse_userauth_banner(m)
+                    ptype, m = self.transport.packetizer.read_message()
+                if ptype == MSG_USERAUTH_GSSAPI_RESPONSE:
+                    # Read the mechanism selected by the server. We send just
+                    # the Kerberos V5 OID, so the server can only respond with
+                    # this OID.
+                    mech = m.get_string()
+                    m = Message()
+                    m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
+                    try:
+                        m.add_string(
+                            sshgss.ssh_init_sec_context(
+                                self.gss_host, mech, self.username
+                            )
+                        )
+                    except GSS_EXCEPTIONS as e:
+                        return self._handle_local_gss_failure(e)
+                    self.transport._send_message(m)
+                    while True:
+                        ptype, m = self.transport.packetizer.read_message()
+                        if ptype == MSG_USERAUTH_GSSAPI_TOKEN:
+                            srv_token = m.get_string()
+                            try:
+                                next_token = sshgss.ssh_init_sec_context(
+                                    self.gss_host,
+                                    mech,
+                                    self.username,
+                                    srv_token,
+                                )
+                            except GSS_EXCEPTIONS as e:
+                                return self._handle_local_gss_failure(e)
+                            # After this step the GSSAPI should not return any
+                            # token. If it does, we keep sending the token to
+                            # the server until no more token is returned.
+                            if next_token is None:
+                                break
+                            else:
+                                m = Message()
+                                m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
+                                m.add_string(next_token)
+                                self.transport.send_message(m)
+                    else:
+                        raise SSHException(
+                            "Received Package: {}".format(MSG_NAMES[ptype])
+                        )
+                    m = Message()
+                    m.add_byte(cMSG_USERAUTH_GSSAPI_MIC)
+                    # send the MIC to the server
+                    m.add_string(sshgss.ssh_get_mic(self.transport.session_id))
+                elif ptype == MSG_USERAUTH_GSSAPI_ERRTOK:
+                    # RFC 4462 says we are not required to implement GSS-API
+                    # error messages.
+                    # See RFC 4462 Section 3.8 in
+                    # http://www.ietf.org/rfc/rfc4462.txt
+                    raise SSHException("Server returned an error token")
+                elif ptype == MSG_USERAUTH_GSSAPI_ERROR:
+                    maj_status = m.get_int()
+                    min_status = m.get_int()
+                    err_msg = m.get_string()
+                    m.get_string()  # Lang tag - discarded
+                    raise SSHException(
+                        """GSS-API Error:
+                    Major Status: {}
+                    Minor Status: {}
+                    Error Message: {}
+                    """.format(
+                            maj_status, min_status, err_msg
+                        )
+                    )
+                elif ptype == MSG_USERAUTH_FAILURE:
+                    self._parse_userauth_failure(m)
+                    return
+                else:
+                    raise SSHException(
+                        "Received Package: {}".format(MSG_NAMES[ptype])
+                    )
+            elif (
+                self.auth_method == "gssapi-keyex"
+                and self.transport.gss_kex_used
+            ):
+                kexgss = self.transport.kexgss_ctxt
+                kexgss.set_username(self.username)
+                mic_token = kexgss.ssh_get_mic(self.transport.session_id)
+                m.add_string(mic_token)
+            elif self.auth_method == "none":
+                pass
+            else:
+                raise SSHException(
+                'Unknown auth method "{}"'.format(self.auth_method)
+                )
+            self.transport._send_message(m)
+        else:
+            self._log(
+                DEBUG, 'Service request "{}" accepted (?)'.format(service)
+            )
+
+
+    def _send_auth_result(self, username, method, result):
+        print("send auth result")
+        # okay, send result
+        m = Message()
+        if result == AUTH_SUCCESSFUL:
+            print("auth successful")
+            self._log(INFO, "Auth granted ({}).".format(method))
+            m.add_byte(cMSG_USERAUTH_SUCCESS)
+            self.authenticated = True
+        else:
+            self._log(INFO, "Auth rejected ({}).".format(method))
+            print("auth rejected")
+            m.add_byte(cMSG_USERAUTH_FAILURE)
+            m.add_string(
+                self.transport.server_object.get_allowed_auths(username)
+            )
+            if result == AUTH_PARTIALLY_SUCCESSFUL:
+                print("auth partially successful")
+                m.add_boolean(True)
+            else:
+                m.add_boolean(False)
+                self.auth_fail_count += 1
+        self.transport._send_message(m)
+        if self.auth_fail_count >= 10:
+            self._disconnect_no_more_auth()
+        if result == AUTH_SUCCESSFUL:
+            self.transport._auth_trigger()
+
+    def _interactive_query(self, q):
+        print("interactive query")
+        # make interactive query instead of response
+        m = Message()
+        m.add_byte(cMSG_USERAUTH_INFO_REQUEST)
+        m.add_string(q.name)
+        m.add_string(q.instructions)
+        m.add_string(bytes())
+        m.add_int(len(q.prompts))
+        for p in q.prompts:
+            m.add_string(p[0])
+            m.add_boolean(p[1])
+        self.transport._send_message(m)
+
+    def _parse_userauth_request(self, m):
+        print("userauth request")
+
+        if not self.transport.server_mode:
+            # er, uh... what?
+            m = Message()
+            m.add_byte(cMSG_USERAUTH_FAILURE)
+            m.add_string("none")
+            m.add_boolean(False)
+            self.transport._send_message(m)
+            return
+        if self.authenticated:
+            # ignore
+            return
+        username = m.get_text()
+        service = m.get_text()
+        method = m.get_text()
+        self._log(
+            DEBUG,
+            "Auth request (type={}) service={}, username={}".format(
+                method, service, username
+            ),
+        )
+        if service != "ssh-connection":
+            self._disconnect_service_not_available()
+            return
+        if (self.auth_username is not None) and (
+            self.auth_username != username
+        ):
+            self._log(
+                WARNING,
+                "Auth rejected because the client attempted to change username in mid-flight",  # noqa
+            )
+            self._disconnect_no_more_auth()
+            return
+        self.auth_username = username
+        # check if GSS-API authentication is enabled
+        gss_auth = self.transport.server_object.enable_auth_gssapi()
+
+        if method == "none":
+            result = self.transport.server_object.check_auth_none(username)
+        elif method == "password":
+            changereq = m.get_boolean()
+            password = m.get_binary()
+            try:
+                password = password.decode("UTF-8")
+            except UnicodeError:
+                # some clients/servers expect non-utf-8 passwords!
+                # in this case, just return the raw byte string.
+                pass
+            if changereq:
+                # always treated as failure, since we don't support changing
+                # passwords, but collect the list of valid auth types from
+                # the callback anyway
+                self._log(DEBUG, "Auth request to change passwords (rejected)")
+                newpassword = m.get_binary()
+                try:
+                    newpassword = newpassword.decode("UTF-8", "replace")
+                except UnicodeError:
+                    pass
+                result = AUTH_FAILED
+            else:
+                result = self.transport.server_object.check_auth_password(
+                    username, password
+                )
+        elif method == "publickey":
+            sig_attached = m.get_boolean()
+            # NOTE: server never wants to guess a client's algo, they're
+            # telling us directly. No need for _finalize_pubkey_algorithm
+            # anywhere in this flow.
+            algorithm = m.get_text()
+            keyblob = m.get_binary()
+            try:
+                key = self._generate_key_from_request(algorithm, keyblob)
+            except SSHException as e:
+                self._log(INFO, "Auth rejected: public key: {}".format(str(e)))
+                key = None
+            except Exception as e:
+                msg = "Auth rejected: unsupported or mangled public key ({}: {})"  # noqa
+                self._log(INFO, msg.format(e.__class__.__name__, e))
+                key = None
+            if key is None:
+                self._disconnect_no_more_auth()
+                return
+            # first check if this key is okay... if not, we can skip the verify
+            result = self.transport.server_object.check_auth_publickey(
+                username, key
+            )
+            if result != AUTH_FAILED:
+                print("auth failed")
+                # key is okay, verify it
+                if not sig_attached:
+                    # client wants to know if this key is acceptable, before it
+                    # signs anything...  send special "ok" message
+                    m = Message()
+                    m.add_byte(cMSG_USERAUTH_PK_OK)
+                    m.add_string(algorithm)
+                    m.add_string(keyblob)
+                    self.transport._send_message(m)
+                    return
+                sig = Message(m.get_binary())
+                # 
+                blob = self._get_session_blob(
+                    key, service, username, algorithm
+                )
+                if not key.verify_ssh_sig(blob, sig):
+                    self._log(INFO, "Auth rejected: invalid signature")
+                    result = AUTH_FAILED
+        elif method == "keyboard-interactive":
+            submethods = m.get_string()
+            result = self.transport.server_object.check_auth_interactive(
+                username, submethods
+            )
+            if isinstance(result, InteractiveQuery):
+                # make interactive query instead of response
+                self._interactive_query(result)
+                return
+        elif method == "gssapi-with-mic" and gss_auth:
+            sshgss = GSSAuth(method)
+            # Read the number of OID mechanisms supported by the client.
+            # OpenSSH sends just one OID. It's the Kerveros V5 OID and that's
+            # the only OID we support.
+            mechs = m.get_int()
+            # We can't accept more than one OID, so if the SSH client sends
+            # more than one, disconnect.
+            if mechs > 1:
+                self._log(
+                    INFO,
+                    "Disconnect: Received more than one GSS-API OID mechanism",
+                )
+                self._disconnect_no_more_auth()
+            desired_mech = m.get_string()
+            mech_ok = sshgss.ssh_check_mech(desired_mech)
+            # if we don't support the mechanism, disconnect.
+            if not mech_ok:
+                self._log(
+                    INFO,
+                    "Disconnect: Received an invalid GSS-API OID mechanism",
+                )
+                self._disconnect_no_more_auth()
+            # send the Kerberos V5 GSSAPI OID to the client
+            supported_mech = sshgss.ssh_gss_oids("server")
+            # RFC 4462 says we are not required to implement GSS-API error
+            # messages. See section 3.8 in http://www.ietf.org/rfc/rfc4462.txt
+            m = Message()
+            m.add_byte(cMSG_USERAUTH_GSSAPI_RESPONSE)
+            m.add_bytes(supported_mech)
+            self.transport.auth_handler = GssapiWithMicAuthHandler(
+                self, sshgss
+            )
+            self.transport._expected_packet = (
+                MSG_USERAUTH_GSSAPI_TOKEN,
+                MSG_USERAUTH_REQUEST,
+                MSG_SERVICE_REQUEST,
+            )
+            self.transport._send_message(m)
+            return
+        elif method == "gssapi-keyex" and gss_auth:
+            mic_token = m.get_string()
+            sshgss = self.transport.kexgss_ctxt
+            if sshgss is None:
+                # If there is no valid context, we reject the authentication
+                result = AUTH_FAILED
+                self._send_auth_result(username, method, result)
+            try:
+                sshgss.ssh_check_mic(
+                    mic_token, self.transport.session_id, self.auth_username
+                )
+            except Exception:
+                result = AUTH_FAILED
+                self._send_auth_result(username, method, result)
+                raise
+            result = AUTH_SUCCESSFUL
+            self.transport.server_object.check_auth_gssapi_keyex(
+                username, result
+            )
+        else:
+            result = self.transport.server_object.check_auth_none(username)
+        # okay, send result
+        self._send_auth_result(username, method, result)
+
+    def _parse_userauth_success(self, m):
+        print("parse_userauth_success")
+        self._log(
+            INFO, "Authentication ({}) successful!".format(self.auth_method)
+        )
+        self.authenticated = True
+        self.transport._auth_trigger()
+        if self.auth_event is not None:
+            self.auth_event.set()
+
+    def _parse_userauth_failure(self, m):
+        print("parse_userauth_failure")
+        authlist = m.get_list()
+        partial = m.get_boolean()
+        print("partial: ", partial)
+        if partial:
+            self._log(INFO, "Authentication continues...")
+            self._log(DEBUG, "Methods: " + str(authlist))
+            self.transport.saved_exception = PartialAuthentication(authlist)
+        elif self.auth_method not in authlist:
+            for msg in (
+                "Authentication type ({}) not permitted.".format(
+                    self.auth_method
+                ),
+                "Allowed methods: {}".format(authlist),
+            ):
+                self._log(DEBUG, msg)
+            self.transport.saved_exception = BadAuthenticationType(
+                "Bad authentication type", authlist
+            )
+        else:
+            self._log(
+                INFO, "Authentication ({}) failed.".format(self.auth_method)
+            )
+        self.authenticated = False
+        self.username = None
+        if self.auth_event is not None:
+            self.auth_event.set()
+
+    def _parse_userauth_banner(self, m):
+        print("parse_userauth_banner")
+        banner = m.get_string()
+        self.banner = banner
+        self._log(INFO, "Auth banner: {}".format(banner))
+        # who cares.
+
+    def _parse_userauth_info_request(self, m):
+        print("parse_userauth_info_request")
+        if self.auth_method != "keyboard-interactive":
+            raise SSHException("Illegal info request from server")
+        title = m.get_text()
+        instructions = m.get_text()
+        m.get_binary()  # lang
+        prompts = m.get_int()
+        prompt_list = []
+        for i in range(prompts):
+            prompt_list.append((m.get_text(), m.get_boolean()))
+        response_list = self.interactive_handler(
+            title, instructions, prompt_list
+        )
+
+        m = Message()
+        m.add_byte(cMSG_USERAUTH_INFO_RESPONSE)
+        m.add_int(len(response_list))
+        for r in response_list:
+            m.add_string(r)
+        self.transport._send_message(m)
+
+    def _parse_userauth_info_response(self, m):
+        print("parse_userauth_info_response")
+        if not self.transport.server_mode:
+            raise SSHException("Illegal info response from server")
+        n = m.get_int()
+        responses = []
+        for i in range(n):
+            responses.append(m.get_text())
+        result = self.transport.server_object.check_auth_interactive_response(
+            responses
+        )
+        if isinstance(result, InteractiveQuery):
+            # make interactive query instead of response
+            self._interactive_query(result)
+            return
+        self._send_auth_result(
+            self.auth_username, "keyboard-interactive", result
+        )
+
+    def _handle_local_gss_failure(self, e):
+        print("handle_local_gss_failure")
+        self.transport.saved_exception = e
+        self._log(DEBUG, "GSSAPI failure: {}".format(e))
+        self._log(INFO, "Authentication ({}) failed.".format(self.auth_method))
+        self.authenticated = False
+        self.username = None
+        if self.auth_event is not None:
+            self.auth_event.set()
+        return
+
+    # TODO: do the same to the other tables, in Transport.
+    # TODO 4.0: MAY make sense to make these tables into actual
+    # classes/instances that can be fed a mode bool or whatever. Or,
+    # alternately (both?) make the message types small classes or enums that
+    # embed this info within themselves (which could also then tidy up the
+    # current 'integer -> human readable short string' stuff in common.py).
+    # TODO: if we do that, also expose 'em publicly.
+
+    # Messages which should be handled _by_ servers (sent by clients)
+    _server_handler_table = {
+        MSG_SERVICE_REQUEST: _parse_service_request,
+        MSG_USERAUTH_REQUEST: _parse_userauth_request,
+        MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response,
+    }
+
+    # Messages which should be handled _by_ clients (sent by servers)
+    _client_handler_table = {
+        MSG_SERVICE_ACCEPT: _parse_service_accept,
+        MSG_USERAUTH_SUCCESS: _parse_userauth_success,
+        MSG_USERAUTH_FAILURE: _parse_userauth_failure,
+        MSG_USERAUTH_BANNER: _parse_userauth_banner,
+        MSG_USERAUTH_INFO_REQUEST: _parse_userauth_info_request,
+    }
+
+    # NOTE: prior to the fix for #1283, this was a static dict instead of a
+    # property. Should be backwards compatible in most/all cases.
+    @property
+    def _handler_table(self):
+        if self.transport.server_mode:
+            return self._server_handler_table
+        else:
+            return self._client_handler_table
+
+
+class GssapiWithMicAuthHandler:
+    """A specialized Auth handler for gssapi-with-mic
+
+    During the GSSAPI token exchange we need a modified dispatch table,
+    because the packet type numbers are not unique.
+    """
+
+    method = "gssapi-with-mic"
+
+    def __init__(self, delegate, sshgss):
+        self._delegate = delegate
+        self.sshgss = sshgss
+
+    def abort(self):
+        self._restore_delegate_auth_handler()
+        return self._delegate.abort()
+
+    @property
+    def transport(self):
+        return self._delegate.transport
+
+    @property
+    def _send_auth_result(self):
+        return self._delegate._send_auth_result
+
+    @property
+    def auth_username(self):
+        return self._delegate.auth_username
+
+    @property
+    def gss_host(self):
+        return self._delegate.gss_host
+
+    def _restore_delegate_auth_handler(self):
+        self.transport.auth_handler = self._delegate
+
+    def _parse_userauth_gssapi_token(self, m):
+        client_token = m.get_string()
+        # use the client token as input to establish a secure
+        # context.
+        sshgss = self.sshgss
+        try:
+            token = sshgss.ssh_accept_sec_context(
+                self.gss_host, client_token, self.auth_username
+            )
+        except Exception as e:
+            self.transport.saved_exception = e
+            result = AUTH_FAILED
+            self._restore_delegate_auth_handler()
+            self._send_auth_result(self.auth_username, self.method, result)
+            raise
+        if token is not None:
+            m = Message()
+            m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
+            m.add_string(token)
+            self.transport._expected_packet = (
+                MSG_USERAUTH_GSSAPI_TOKEN,
+                MSG_USERAUTH_GSSAPI_MIC,
+                MSG_USERAUTH_REQUEST,
+            )
+            self.transport._send_message(m)
+
+    def _parse_userauth_gssapi_mic(self, m):
+        mic_token = m.get_string()
+        sshgss = self.sshgss
+        username = self.auth_username
+        self._restore_delegate_auth_handler()
+        try:
+            sshgss.ssh_check_mic(
+                mic_token, self.transport.session_id, username
+            )
+        except Exception as e:
+            self.transport.saved_exception = e
+            result = AUTH_FAILED
+            self._send_auth_result(username, self.method, result)
+            raise
+        # TODO: Implement client credential saving.
+        # The OpenSSH server is able to create a TGT with the delegated
+        # client credentials, but this is not supported by GSS-API.
+        result = AUTH_SUCCESSFUL
+        self.transport.server_object.check_auth_gssapi_with_mic(
+            username, result
+        )
+        # okay, send result
+        self._send_auth_result(username, self.method, result)
+
+    def _parse_service_request(self, m):
+        print("parse service request")
+        self._restore_delegate_auth_handler()
+        return self._delegate._parse_service_request(m)
+
+    def _parse_userauth_request(self, m):
+        self._restore_delegate_auth_handler()
+        return self._delegate._parse_userauth_request(m)
+
+    __handler_table = {
+        MSG_SERVICE_REQUEST: _parse_service_request,
+        MSG_USERAUTH_REQUEST: _parse_userauth_request,
+        MSG_USERAUTH_GSSAPI_TOKEN: _parse_userauth_gssapi_token,
+        MSG_USERAUTH_GSSAPI_MIC: _parse_userauth_gssapi_mic,
+    }
+
+    @property
+    def _handler_table(self):
+        # TODO: determine if we can cut this up like we did for the primary
+        # AuthHandler class.
+        return self.__handler_table
diff --git a/paramiko/ber.py b/paramiko/ber.py
new file mode 100644
index 0000000..b8287f5
--- /dev/null
+++ b/paramiko/ber.py
@@ -0,0 +1,139 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+from paramiko.common import max_byte, zero_byte, byte_ord, byte_chr
+
+import paramiko.util as util
+from paramiko.util import b
+from paramiko.sftp import int64
+
+
+class BERException(Exception):
+    pass
+
+
+class BER:
+    """
+    Robey's tiny little attempt at a BER decoder.
+    """
+
+    def __init__(self, content=bytes()):
+        self.content = b(content)
+        self.idx = 0
+
+    def asbytes(self):
+        return self.content
+
+    def __str__(self):
+        return self.asbytes()
+
+    def __repr__(self):
+        return "BER('" + repr(self.content) + "')"
+
+    def decode(self):
+        return self.decode_next()
+
+    def decode_next(self):
+        if self.idx >= len(self.content):
+            return None
+        ident = byte_ord(self.content[self.idx])
+        self.idx += 1
+        if (ident & 31) == 31:
+            # identifier > 30
+            ident = 0
+            while self.idx < len(self.content):
+                t = byte_ord(self.content[self.idx])
+                self.idx += 1
+                ident = (ident << 7) | (t & 0x7F)
+                if not (t & 0x80):
+                    break
+        if self.idx >= len(self.content):
+            return None
+        # now fetch length
+        size = byte_ord(self.content[self.idx])
+        self.idx += 1
+        if size & 0x80:
+            # more complimicated...
+            # FIXME: theoretically should handle indefinite-length (0x80)
+            t = size & 0x7F
+            if self.idx + t > len(self.content):
+                return None
+            size = util.inflate_long(
+                self.content[self.idx : self.idx + t], True
+            )
+            self.idx += t
+        if self.idx + size > len(self.content):
+            # can't fit
+            return None
+        data = self.content[self.idx : self.idx + size]
+        self.idx += size
+        # now switch on id
+        if ident == 0x30:
+            # sequence
+            return self.decode_sequence(data)
+        elif ident == 2:
+            # int
+            return util.inflate_long(data)
+        else:
+            # 1: boolean (00 false, otherwise true)
+            msg = "Unknown ber encoding type {:d} (robey is lazy)"
+            raise BERException(msg.format(ident))
+
+    @staticmethod
+    def decode_sequence(data):
+        out = []
+        ber = BER(data)
+        while True:
+            x = ber.decode_next()
+            if x is None:
+                break
+            out.append(x)
+        return out
+
+    def encode_tlv(self, ident, val):
+        # no need to support ident > 31 here
+        self.content += byte_chr(ident)
+        if len(val) > 0x7F:
+            lenstr = util.deflate_long(len(val))
+            self.content += byte_chr(0x80 + len(lenstr)) + lenstr
+        else:
+            self.content += byte_chr(len(val))
+        self.content += val
+
+    def encode(self, x):
+        if type(x) is bool:
+            if x:
+                self.encode_tlv(1, max_byte)
+            else:
+                self.encode_tlv(1, zero_byte)
+        elif (type(x) is int) or (type(x) is int64):
+            self.encode_tlv(2, util.deflate_long(x))
+        elif type(x) is str:
+            self.encode_tlv(4, x)
+        elif (type(x) is list) or (type(x) is tuple):
+            self.encode_tlv(0x30, self.encode_sequence(x))
+        else:
+            raise BERException(
+                "Unknown type for encoding: {!r}".format(type(x))
+            )
+
+    @staticmethod
+    def encode_sequence(data):
+        ber = BER()
+        for item in data:
+            ber.encode(item)
+        return ber.asbytes()
diff --git a/paramiko/buffered_pipe.py b/paramiko/buffered_pipe.py
new file mode 100644
index 0000000..c19279c
--- /dev/null
+++ b/paramiko/buffered_pipe.py
@@ -0,0 +1,212 @@
+# Copyright (C) 2006-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Attempt to generalize the "feeder" part of a `.Channel`: an object which can be
+read from and closed, but is reading from a buffer fed by another thread.  The
+read operations are blocking and can have a timeout set.
+"""
+
+import array
+import threading
+import time
+from paramiko.util import b
+
+
+class PipeTimeout(IOError):
+    """
+    Indicates that a timeout was reached on a read from a `.BufferedPipe`.
+    """
+
+    pass
+
+
+class BufferedPipe:
+    """
+    A buffer that obeys normal read (with timeout) & close semantics for a
+    file or socket, but is fed data from another thread.  This is used by
+    `.Channel`.
+    """
+
+    def __init__(self):
+        self._lock = threading.Lock()
+        self._cv = threading.Condition(self._lock)
+        self._event = None
+        self._buffer = array.array("B")
+        self._closed = False
+
+    def _buffer_frombytes(self, data):
+        self._buffer.frombytes(data)
+
+    def _buffer_tobytes(self, limit=None):
+        return self._buffer[:limit].tobytes()
+
+    def set_event(self, event):
+        """
+        Set an event on this buffer.  When data is ready to be read (or the
+        buffer has been closed), the event will be set.  When no data is
+        ready, the event will be cleared.
+
+        :param threading.Event event: the event to set/clear
+        """
+        self._lock.acquire()
+        try:
+            self._event = event
+            # Make sure the event starts in `set` state if we appear to already
+            # be closed; otherwise, if we start in `clear` state & are closed,
+            # nothing will ever call `.feed` and the event (& OS pipe, if we're
+            # wrapping one - see `Channel.fileno`) will permanently stay in
+            # `clear`, causing deadlock if e.g. `select`ed upon.
+            if self._closed or len(self._buffer) > 0:
+                event.set()
+            else:
+                event.clear()
+        finally:
+            self._lock.release()
+
+    def feed(self, data):
+        """
+        Feed new data into this pipe.  This method is assumed to be called
+        from a separate thread, so synchronization is done.
+
+        :param data: the data to add, as a ``str`` or ``bytes``
+        """
+        self._lock.acquire()
+        try:
+            if self._event is not None:
+                self._event.set()
+            self._buffer_frombytes(b(data))
+            self._cv.notify_all()
+        finally:
+            self._lock.release()
+
+    def read_ready(self):
+        """
+        Returns true if data is buffered and ready to be read from this
+        feeder.  A ``False`` result does not mean that the feeder has closed;
+        it means you may need to wait before more data arrives.
+
+        :return:
+            ``True`` if a `read` call would immediately return at least one
+            byte; ``False`` otherwise.
+        """
+        self._lock.acquire()
+        try:
+            if len(self._buffer) == 0:
+                return False
+            return True
+        finally:
+            self._lock.release()
+
+    def read(self, nbytes, timeout=None):
+        """
+        Read data from the pipe.  The return value is a string representing
+        the data received.  The maximum amount of data to be received at once
+        is specified by ``nbytes``.  If a string of length zero is returned,
+        the pipe has been closed.
+
+        The optional ``timeout`` argument can be a nonnegative float expressing
+        seconds, or ``None`` for no timeout.  If a float is given, a
+        `.PipeTimeout` will be raised if the timeout period value has elapsed
+        before any data arrives.
+
+        :param int nbytes: maximum number of bytes to read
+        :param float timeout:
+            maximum seconds to wait (or ``None``, the default, to wait forever)
+        :return: the read data, as a ``str`` or ``bytes``
+
+        :raises:
+            `.PipeTimeout` -- if a timeout was specified and no data was ready
+            before that timeout
+        """
+        out = bytes()
+        self._lock.acquire()
+        try:
+            if len(self._buffer) == 0:
+                if self._closed:
+                    return out
+                # should we block?
+                if timeout == 0.0:
+                    raise PipeTimeout()
+                # loop here in case we get woken up but a different thread has
+                # grabbed everything in the buffer.
+                while (len(self._buffer) == 0) and not self._closed:
+                    then = time.time()
+                    self._cv.wait(timeout)
+                    if timeout is not None:
+                        timeout -= time.time() - then
+                        if timeout <= 0.0:
+                            raise PipeTimeout()
+
+            # something's in the buffer and we have the lock!
+            if len(self._buffer) <= nbytes:
+                out = self._buffer_tobytes()
+                del self._buffer[:]
+                if (self._event is not None) and not self._closed:
+                    self._event.clear()
+            else:
+                out = self._buffer_tobytes(nbytes)
+                del self._buffer[:nbytes]
+        finally:
+            self._lock.release()
+
+        return out
+
+    def empty(self):
+        """
+        Clear out the buffer and return all data that was in it.
+
+        :return:
+            any data that was in the buffer prior to clearing it out, as a
+            `str`
+        """
+        self._lock.acquire()
+        try:
+            out = self._buffer_tobytes()
+            del self._buffer[:]
+            if (self._event is not None) and not self._closed:
+                self._event.clear()
+            return out
+        finally:
+            self._lock.release()
+
+    def close(self):
+        """
+        Close this pipe object.  Future calls to `read` after the buffer
+        has been emptied will return immediately with an empty string.
+        """
+        self._lock.acquire()
+        try:
+            self._closed = True
+            self._cv.notify_all()
+            if self._event is not None:
+                self._event.set()
+        finally:
+            self._lock.release()
+
+    def __len__(self):
+        """
+        Return the number of bytes buffered.
+
+        :return: number (`int`) of bytes buffered
+        """
+        self._lock.acquire()
+        try:
+            return len(self._buffer)
+        finally:
+            self._lock.release()
diff --git a/paramiko/channel.py b/paramiko/channel.py
new file mode 100644
index 0000000..2757450
--- /dev/null
+++ b/paramiko/channel.py
@@ -0,0 +1,1390 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Abstraction for an SSH2 channel.
+"""
+
+import binascii
+import os
+import socket
+import time
+import threading
+
+from functools import wraps
+
+from paramiko import util
+from paramiko.common import (
+    cMSG_CHANNEL_REQUEST,
+    cMSG_CHANNEL_WINDOW_ADJUST,
+    cMSG_CHANNEL_DATA,
+    cMSG_CHANNEL_EXTENDED_DATA,
+    DEBUG,
+    ERROR,
+    cMSG_CHANNEL_SUCCESS,
+    cMSG_CHANNEL_FAILURE,
+    cMSG_CHANNEL_EOF,
+    cMSG_CHANNEL_CLOSE,
+)
+from paramiko.message import Message
+from paramiko.ssh_exception import SSHException
+from paramiko.file import BufferedFile
+from paramiko.buffered_pipe import BufferedPipe, PipeTimeout
+from paramiko import pipe
+from paramiko.util import ClosingContextManager
+
+
+def open_only(func):
+    """
+    Decorator for `.Channel` methods which performs an openness check.
+
+    :raises:
+        `.SSHException` -- If the wrapped method is called on an unopened
+        `.Channel`.
+    """
+
+    @wraps(func)
+    def _check(self, *args, **kwds):
+        if (
+            self.closed
+            or self.eof_received
+            or self.eof_sent
+            or not self.active
+        ):
+            raise SSHException("Channel is not open")
+        return func(self, *args, **kwds)
+
+    return _check
+
+
+class Channel(ClosingContextManager):
+    """
+    A secure tunnel across an SSH `.Transport`.  A Channel is meant to behave
+    like a socket, and has an API that should be indistinguishable from the
+    Python socket API.
+
+    Because SSH2 has a windowing kind of flow control, if you stop reading data
+    from a Channel and its buffer fills up, the server will be unable to send
+    you any more data until you read some of it.  (This won't affect other
+    channels on the same transport -- all channels on a single transport are
+    flow-controlled independently.)  Similarly, if the server isn't reading
+    data you send, calls to `send` may block, unless you set a timeout.  This
+    is exactly like a normal network socket, so it shouldn't be too surprising.
+
+    Instances of this class may be used as context managers.
+    """
+
+    def __init__(self, chanid):
+        """
+        Create a new channel.  The channel is not associated with any
+        particular session or `.Transport` until the Transport attaches it.
+        Normally you would only call this method from the constructor of a
+        subclass of `.Channel`.
+
+        :param int chanid:
+            the ID of this channel, as passed by an existing `.Transport`.
+        """
+        #: Channel ID
+        self.chanid = chanid
+        #: Remote channel ID
+        self.remote_chanid = 0
+        #: `.Transport` managing this channel
+        self.transport = None
+        #: Whether the connection is presently active
+        self.active = False
+        self.eof_received = 0
+        self.eof_sent = 0
+        self.in_buffer = BufferedPipe()
+        self.in_stderr_buffer = BufferedPipe()
+        self.timeout = None
+        #: Whether the connection has been closed
+        self.closed = False
+        self.ultra_debug = False
+        self.lock = threading.Lock()
+        self.out_buffer_cv = threading.Condition(self.lock)
+        self.in_window_size = 0
+        self.out_window_size = 0
+        self.in_max_packet_size = 0
+        self.out_max_packet_size = 0
+        self.in_window_threshold = 0
+        self.in_window_sofar = 0
+        self.status_event = threading.Event()
+        self._name = str(chanid)
+        self.logger = util.get_logger("paramiko.transport")
+        self._pipe = None
+        self.event = threading.Event()
+        self.event_ready = False
+        self.combine_stderr = False
+        self.exit_status = -1
+        self.origin_addr = None
+
+    def __del__(self):
+        try:
+            self.close()
+        except:
+            pass
+
+    def __repr__(self):
+        """
+        Return a string representation of this object, for debugging.
+        """
+        out = "<paramiko.Channel {}".format(self.chanid)
+        if self.closed:
+            out += " (closed)"
+        elif self.active:
+            if self.eof_received:
+                out += " (EOF received)"
+            if self.eof_sent:
+                out += " (EOF sent)"
+            out += " (open) window={}".format(self.out_window_size)
+            if len(self.in_buffer) > 0:
+                out += " in-buffer={}".format(len(self.in_buffer))
+        out += " -> " + repr(self.transport)
+        out += ">"
+        return out
+
+    @open_only
+    def get_pty(
+        self,
+        term="vt100",
+        width=80,
+        height=24,
+        width_pixels=0,
+        height_pixels=0,
+    ):
+        """
+        Request a pseudo-terminal from the server.  This is usually used right
+        after creating a client channel, to ask the server to provide some
+        basic terminal semantics for a shell invoked with `invoke_shell`.
+        It isn't necessary (or desirable) to call this method if you're going
+        to execute a single command with `exec_command`.
+
+        :param str term: the terminal type to emulate
+            (for example, ``'vt100'``)
+        :param int width: width (in characters) of the terminal screen
+        :param int height: height (in characters) of the terminal screen
+        :param int width_pixels: width (in pixels) of the terminal screen
+        :param int height_pixels: height (in pixels) of the terminal screen
+
+        :raises:
+            `.SSHException` -- if the request was rejected or the channel was
+            closed
+        """
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("pty-req")
+        m.add_boolean(True)
+        m.add_string(term)
+        m.add_int(width)
+        m.add_int(height)
+        m.add_int(width_pixels)
+        m.add_int(height_pixels)
+        m.add_string(bytes())
+        self._event_pending()
+        self.transport._send_user_message(m)
+        self._wait_for_event()
+
+    @open_only
+    def invoke_shell(self):
+        """
+        Request an interactive shell session on this channel.  If the server
+        allows it, the channel will then be directly connected to the stdin,
+        stdout, and stderr of the shell.
+
+        Normally you would call `get_pty` before this, in which case the
+        shell will operate through the pty, and the channel will be connected
+        to the stdin and stdout of the pty.
+
+        When the shell exits, the channel will be closed and can't be reused.
+        You must open a new channel if you wish to open another shell.
+
+        :raises:
+            `.SSHException` -- if the request was rejected or the channel was
+            closed
+        """
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("shell")
+        m.add_boolean(True)
+        self._event_pending()
+        self.transport._send_user_message(m)
+        self._wait_for_event()
+
+    @open_only
+    def exec_command(self, command):
+        """
+        Execute a command on the server.  If the server allows it, the channel
+        will then be directly connected to the stdin, stdout, and stderr of
+        the command being executed.
+
+        When the command finishes executing, the channel will be closed and
+        can't be reused.  You must open a new channel if you wish to execute
+        another command.
+
+        :param str command: a shell command to execute.
+
+        :raises:
+            `.SSHException` -- if the request was rejected or the channel was
+            closed
+        """
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("exec")
+        m.add_boolean(True)
+        m.add_string(command)
+        self._event_pending()
+        self.transport._send_user_message(m)
+        self._wait_for_event()
+
+    @open_only
+    def invoke_subsystem(self, subsystem):
+        """
+        Request a subsystem on the server (for example, ``sftp``).  If the
+        server allows it, the channel will then be directly connected to the
+        requested subsystem.
+
+        When the subsystem finishes, the channel will be closed and can't be
+        reused.
+
+        :param str subsystem: name of the subsystem being requested.
+
+        :raises:
+            `.SSHException` -- if the request was rejected or the channel was
+            closed
+        """
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("subsystem")
+        m.add_boolean(True)
+        m.add_string(subsystem)
+        self._event_pending()
+        self.transport._send_user_message(m)
+        self._wait_for_event()
+
+    @open_only
+    def resize_pty(self, width=80, height=24, width_pixels=0, height_pixels=0):
+        """
+        Resize the pseudo-terminal.  This can be used to change the width and
+        height of the terminal emulation created in a previous `get_pty` call.
+
+        :param int width: new width (in characters) of the terminal screen
+        :param int height: new height (in characters) of the terminal screen
+        :param int width_pixels: new width (in pixels) of the terminal screen
+        :param int height_pixels: new height (in pixels) of the terminal screen
+
+        :raises:
+            `.SSHException` -- if the request was rejected or the channel was
+            closed
+        """
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("window-change")
+        m.add_boolean(False)
+        m.add_int(width)
+        m.add_int(height)
+        m.add_int(width_pixels)
+        m.add_int(height_pixels)
+        self.transport._send_user_message(m)
+
+    @open_only
+    def update_environment(self, environment):
+        """
+        Updates this channel's remote shell environment.
+
+        .. note::
+            This operation is additive - i.e. the current environment is not
+            reset before the given environment variables are set.
+
+        .. warning::
+            Servers may silently reject some environment variables; see the
+            warning in `set_environment_variable` for details.
+
+        :param dict environment:
+            a dictionary containing the name and respective values to set
+        :raises:
+            `.SSHException` -- if any of the environment variables was rejected
+            by the server or the channel was closed
+        """
+        for name, value in environment.items():
+            try:
+                self.set_environment_variable(name, value)
+            except SSHException as e:
+                err = 'Failed to set environment variable "{}".'
+                raise SSHException(err.format(name), e)
+
+    @open_only
+    def set_environment_variable(self, name, value):
+        """
+        Set the value of an environment variable.
+
+        .. warning::
+            The server may reject this request depending on its ``AcceptEnv``
+            setting; such rejections will fail silently (which is common client
+            practice for this particular request type). Make sure you
+            understand your server's configuration before using!
+
+        :param str name: name of the environment variable
+        :param str value: value of the environment variable
+
+        :raises:
+            `.SSHException` -- if the request was rejected or the channel was
+            closed
+        """
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("env")
+        m.add_boolean(False)
+        m.add_string(name)
+        m.add_string(value)
+        self.transport._send_user_message(m)
+
+    def exit_status_ready(self):
+        """
+        Return true if the remote process has exited and returned an exit
+        status. You may use this to poll the process status if you don't
+        want to block in `recv_exit_status`. Note that the server may not
+        return an exit status in some cases (like bad servers).
+
+        :return:
+            ``True`` if `recv_exit_status` will return immediately, else
+            ``False``.
+
+        .. versionadded:: 1.7.3
+        """
+        return self.closed or self.status_event.is_set()
+
+    def recv_exit_status(self):
+        """
+        Return the exit status from the process on the server.  This is
+        mostly useful for retrieving the results of an `exec_command`.
+        If the command hasn't finished yet, this method will wait until
+        it does, or until the channel is closed.  If no exit status is
+        provided by the server, -1 is returned.
+
+        .. warning::
+            In some situations, receiving remote output larger than the current
+            `.Transport` or session's ``window_size`` (e.g. that set by the
+            ``default_window_size`` kwarg for `.Transport.__init__`) will cause
+            `.recv_exit_status` to hang indefinitely if it is called prior to a
+            sufficiently large `.Channel.recv` (or if there are no threads
+            calling `.Channel.recv` in the background).
+
+            In these cases, ensuring that `.recv_exit_status` is called *after*
+            `.Channel.recv` (or, again, using threads) can avoid the hang.
+
+        :return: the exit code (as an `int`) of the process on the server.
+
+        .. versionadded:: 1.2
+        """
+        self.status_event.wait()
+        assert self.status_event.is_set()
+        return self.exit_status
+
+    def send_exit_status(self, status):
+        """
+        Send the exit status of an executed command to the client.  (This
+        really only makes sense in server mode.)  Many clients expect to
+        get some sort of status code back from an executed command after
+        it completes.
+
+        :param int status: the exit code of the process
+
+        .. versionadded:: 1.2
+        """
+        # in many cases, the channel will not still be open here.
+        # that's fine.
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("exit-status")
+        m.add_boolean(False)
+        m.add_int(status)
+        self.transport._send_user_message(m)
+
+    @open_only
+    def request_x11(
+        self,
+        screen_number=0,
+        auth_protocol=None,
+        auth_cookie=None,
+        single_connection=False,
+        handler=None,
+    ):
+        """
+        Request an x11 session on this channel.  If the server allows it,
+        further x11 requests can be made from the server to the client,
+        when an x11 application is run in a shell session.
+
+        From :rfc:`4254`::
+
+            It is RECOMMENDED that the 'x11 authentication cookie' that is
+            sent be a fake, random cookie, and that the cookie be checked and
+            replaced by the real cookie when a connection request is received.
+
+        If you omit the auth_cookie, a new secure random 128-bit value will be
+        generated, used, and returned.  You will need to use this value to
+        verify incoming x11 requests and replace them with the actual local
+        x11 cookie (which requires some knowledge of the x11 protocol).
+
+        If a handler is passed in, the handler is called from another thread
+        whenever a new x11 connection arrives.  The default handler queues up
+        incoming x11 connections, which may be retrieved using
+        `.Transport.accept`.  The handler's calling signature is::
+
+            handler(channel: Channel, (address: str, port: int))
+
+        :param int screen_number: the x11 screen number (0, 10, etc.)
+        :param str auth_protocol:
+            the name of the X11 authentication method used; if none is given,
+            ``"MIT-MAGIC-COOKIE-1"`` is used
+        :param str auth_cookie:
+            hexadecimal string containing the x11 auth cookie; if none is
+            given, a secure random 128-bit value is generated
+        :param bool single_connection:
+            if True, only a single x11 connection will be forwarded (by
+            default, any number of x11 connections can arrive over this
+            session)
+        :param handler:
+            an optional callable handler to use for incoming X11 connections
+        :return: the auth_cookie used
+        """
+        if auth_protocol is None:
+            auth_protocol = "MIT-MAGIC-COOKIE-1"
+        if auth_cookie is None:
+            auth_cookie = binascii.hexlify(os.urandom(16))
+
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("x11-req")
+        m.add_boolean(True)
+        m.add_boolean(single_connection)
+        m.add_string(auth_protocol)
+        m.add_string(auth_cookie)
+        m.add_int(screen_number)
+        self._event_pending()
+        self.transport._send_user_message(m)
+        self._wait_for_event()
+        self.transport._set_x11_handler(handler)
+        return auth_cookie
+
+    @open_only
+    def request_forward_agent(self, handler):
+        """
+        Request for a forward SSH Agent on this channel.
+        This is only valid for an ssh-agent from OpenSSH !!!
+
+        :param handler:
+            a required callable handler to use for incoming SSH Agent
+            connections
+
+        :return: True if we are ok, else False
+            (at that time we always return ok)
+
+        :raises: SSHException in case of channel problem.
+        """
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_REQUEST)
+        m.add_int(self.remote_chanid)
+        m.add_string("auth-agent-req@openssh.com")
+        m.add_boolean(False)
+        self.transport._send_user_message(m)
+        self.transport._set_forward_agent_handler(handler)
+        return True
+
+    def get_transport(self):
+        """
+        Return the `.Transport` associated with this channel.
+        """
+        return self.transport
+
+    def set_name(self, name):
+        """
+        Set a name for this channel.  Currently it's only used to set the name
+        of the channel in logfile entries.  The name can be fetched with the
+        `get_name` method.
+
+        :param str name: new channel name
+        """
+        self._name = name
+
+    def get_name(self):
+        """
+        Get the name of this channel that was previously set by `set_name`.
+        """
+        return self._name
+
+    def get_id(self):
+        """
+        Return the `int` ID # for this channel.
+
+        The channel ID is unique across a `.Transport` and usually a small
+        number.  It's also the number passed to
+        `.ServerInterface.check_channel_request` when determining whether to
+        accept a channel request in server mode.
+        """
+        return self.chanid
+
+    def set_combine_stderr(self, combine):
+        """
+        Set whether stderr should be combined into stdout on this channel.
+        The default is ``False``, but in some cases it may be convenient to
+        have both streams combined.
+
+        If this is ``False``, and `exec_command` is called (or ``invoke_shell``
+        with no pty), output to stderr will not show up through the `recv`
+        and `recv_ready` calls.  You will have to use `recv_stderr` and
+        `recv_stderr_ready` to get stderr output.
+
+        If this is ``True``, data will never show up via `recv_stderr` or
+        `recv_stderr_ready`.
+
+        :param bool combine:
+            ``True`` if stderr output should be combined into stdout on this
+            channel.
+        :return: the previous setting (a `bool`).
+
+        .. versionadded:: 1.1
+        """
+        data = bytes()
+        self.lock.acquire()
+        try:
+            old = self.combine_stderr
+            self.combine_stderr = combine
+            if combine and not old:
+                # copy old stderr buffer into primary buffer
+                data = self.in_stderr_buffer.empty()
+        finally:
+            self.lock.release()
+        if len(data) > 0:
+            self._feed(data)
+        return old
+
+    # ...socket API...
+
+    def settimeout(self, timeout):
+        """
+        Set a timeout on blocking read/write operations.  The ``timeout``
+        argument can be a nonnegative float expressing seconds, or ``None``.
+        If a float is given, subsequent channel read/write operations will
+        raise a timeout exception if the timeout period value has elapsed
+        before the operation has completed.  Setting a timeout of ``None``
+        disables timeouts on socket operations.
+
+        ``chan.settimeout(0.0)`` is equivalent to ``chan.setblocking(0)``;
+        ``chan.settimeout(None)`` is equivalent to ``chan.setblocking(1)``.
+
+        :param float timeout:
+            seconds to wait for a pending read/write operation before raising
+            ``socket.timeout``, or ``None`` for no timeout.
+        """
+        self.timeout = timeout
+
+    def gettimeout(self):
+        """
+        Returns the timeout in seconds (as a float) associated with socket
+        operations, or ``None`` if no timeout is set.  This reflects the last
+        call to `setblocking` or `settimeout`.
+        """
+        return self.timeout
+
+    def setblocking(self, blocking):
+        """
+        Set blocking or non-blocking mode of the channel: if ``blocking`` is 0,
+        the channel is set to non-blocking mode; otherwise it's set to blocking
+        mode. Initially all channels are in blocking mode.
+
+        In non-blocking mode, if a `recv` call doesn't find any data, or if a
+        `send` call can't immediately dispose of the data, an error exception
+        is raised. In blocking mode, the calls block until they can proceed. An
+        EOF condition is considered "immediate data" for `recv`, so if the
+        channel is closed in the read direction, it will never block.
+
+        ``chan.setblocking(0)`` is equivalent to ``chan.settimeout(0)``;
+        ``chan.setblocking(1)`` is equivalent to ``chan.settimeout(None)``.
+
+        :param int blocking:
+            0 to set non-blocking mode; non-0 to set blocking mode.
+        """
+        if blocking:
+            self.settimeout(None)
+        else:
+            self.settimeout(0.0)
+
+    def getpeername(self):
+        """
+        Return the address of the remote side of this Channel, if possible.
+
+        This simply wraps `.Transport.getpeername`, used to provide enough of a
+        socket-like interface to allow asyncore to work. (asyncore likes to
+        call ``'getpeername'``.)
+        """
+        return self.transport.getpeername()
+
+    def close(self):
+        """
+        Close the channel.  All future read/write operations on the channel
+        will fail.  The remote end will receive no more data (after queued data
+        is flushed).  Channels are automatically closed when their `.Transport`
+        is closed or when they are garbage collected.
+        """
+        self.lock.acquire()
+        try:
+            # only close the pipe when the user explicitly closes the channel.
+            # otherwise they will get unpleasant surprises.  (and do it before
+            # checking self.closed, since the remote host may have already
+            # closed the connection.)
+            if self._pipe is not None:
+                self._pipe.close()
+                self._pipe = None
+
+            if not self.active or self.closed:
+                return
+            msgs = self._close_internal()
+        finally:
+            self.lock.release()
+        for m in msgs:
+            if m is not None:
+                self.transport._send_user_message(m)
+
+    def recv_ready(self):
+        """
+        Returns true if data is buffered and ready to be read from this
+        channel.  A ``False`` result does not mean that the channel has closed;
+        it means you may need to wait before more data arrives.
+
+        :return:
+            ``True`` if a `recv` call on this channel would immediately return
+            at least one byte; ``False`` otherwise.
+        """
+        return self.in_buffer.read_ready()
+
+    def recv(self, nbytes):
+        """
+        Receive data from the channel.  The return value is a string
+        representing the data received.  The maximum amount of data to be
+        received at once is specified by ``nbytes``.  If a string of
+        length zero is returned, the channel stream has closed.
+
+        :param int nbytes: maximum number of bytes to read.
+        :return: received data, as a `bytes`.
+
+        :raises socket.timeout:
+            if no data is ready before the timeout set by `settimeout`.
+        """
+        try:
+            out = self.in_buffer.read(nbytes, self.timeout)
+        except PipeTimeout:
+            raise socket.timeout()
+
+        ack = self._check_add_window(len(out))
+        # no need to hold the channel lock when sending this
+        if ack > 0:
+            m = Message()
+            m.add_byte(cMSG_CHANNEL_WINDOW_ADJUST)
+            m.add_int(self.remote_chanid)
+            m.add_int(ack)
+            self.transport._send_user_message(m)
+
+        return out
+
+    def recv_stderr_ready(self):
+        """
+        Returns true if data is buffered and ready to be read from this
+        channel's stderr stream.  Only channels using `exec_command` or
+        `invoke_shell` without a pty will ever have data on the stderr
+        stream.
+
+        :return:
+            ``True`` if a `recv_stderr` call on this channel would immediately
+            return at least one byte; ``False`` otherwise.
+
+        .. versionadded:: 1.1
+        """
+        return self.in_stderr_buffer.read_ready()
+
+    def recv_stderr(self, nbytes):
+        """
+        Receive data from the channel's stderr stream.  Only channels using
+        `exec_command` or `invoke_shell` without a pty will ever have data
+        on the stderr stream.  The return value is a string representing the
+        data received.  The maximum amount of data to be received at once is
+        specified by ``nbytes``.  If a string of length zero is returned, the
+        channel stream has closed.
+
+        :param int nbytes: maximum number of bytes to read.
+        :return: received data as a `bytes`
+
+        :raises socket.timeout: if no data is ready before the timeout set by
+            `settimeout`.
+
+        .. versionadded:: 1.1
+        """
+        try:
+            out = self.in_stderr_buffer.read(nbytes, self.timeout)
+        except PipeTimeout:
+            raise socket.timeout()
+
+        ack = self._check_add_window(len(out))
+        # no need to hold the channel lock when sending this
+        if ack > 0:
+            m = Message()
+            m.add_byte(cMSG_CHANNEL_WINDOW_ADJUST)
+            m.add_int(self.remote_chanid)
+            m.add_int(ack)
+            self.transport._send_user_message(m)
+
+        return out
+
+    def send_ready(self):
+        """
+        Returns true if data can be written to this channel without blocking.
+        This means the channel is either closed (so any write attempt would
+        return immediately) or there is at least one byte of space in the
+        outbound buffer. If there is at least one byte of space in the
+        outbound buffer, a `send` call will succeed immediately and return
+        the number of bytes actually written.
+
+        :return:
+            ``True`` if a `send` call on this channel would immediately succeed
+            or fail
+        """
+        self.lock.acquire()
+        try:
+            if self.closed or self.eof_sent:
+                return True
+            return self.out_window_size > 0
+        finally:
+            self.lock.release()
+
+    def send(self, s):
+        """
+        Send data to the channel.  Returns the number of bytes sent, or 0 if
+        the channel stream is closed.  Applications are responsible for
+        checking that all data has been sent: if only some of the data was
+        transmitted, the application needs to attempt delivery of the remaining
+        data.
+
+        :param bytes s: data to send
+        :return: number of bytes actually sent, as an `int`
+
+        :raises socket.timeout: if no data could be sent before the timeout set
+            by `settimeout`.
+        """
+
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_DATA)
+        m.add_int(self.remote_chanid)
+        return self._send(s, m)
+
+    def send_stderr(self, s):
+        """
+        Send data to the channel on the "stderr" stream.  This is normally
+        only used by servers to send output from shell commands -- clients
+        won't use this.  Returns the number of bytes sent, or 0 if the channel
+        stream is closed.  Applications are responsible for checking that all
+        data has been sent: if only some of the data was transmitted, the
+        application needs to attempt delivery of the remaining data.
+
+        :param bytes s: data to send.
+        :return: number of bytes actually sent, as an `int`.
+
+        :raises socket.timeout:
+            if no data could be sent before the timeout set by `settimeout`.
+
+        .. versionadded:: 1.1
+        """
+
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_EXTENDED_DATA)
+        m.add_int(self.remote_chanid)
+        m.add_int(1)
+        return self._send(s, m)
+
+    def sendall(self, s):
+        """
+        Send data to the channel, without allowing partial results.  Unlike
+        `send`, this method continues to send data from the given string until
+        either all data has been sent or an error occurs.  Nothing is returned.
+
+        :param bytes s: data to send.
+
+        :raises socket.timeout:
+            if sending stalled for longer than the timeout set by `settimeout`.
+        :raises socket.error:
+            if an error occurred before the entire string was sent.
+
+        .. note::
+            If the channel is closed while only part of the data has been
+            sent, there is no way to determine how much data (if any) was sent.
+            This is irritating, but identically follows Python's API.
+        """
+        while s:
+            sent = self.send(s)
+            s = s[sent:]
+        return None
+
+    def sendall_stderr(self, s):
+        """
+        Send data to the channel's "stderr" stream, without allowing partial
+        results.  Unlike `send_stderr`, this method continues to send data
+        from the given bytestring until all data has been sent or an error
+        occurs. Nothing is returned.
+
+        :param bytes s: data to send to the client as "stderr" output.
+
+        :raises socket.timeout:
+            if sending stalled for longer than the timeout set by `settimeout`.
+        :raises socket.error:
+            if an error occurred before the entire string was sent.
+
+        .. versionadded:: 1.1
+        """
+        while s:
+            sent = self.send_stderr(s)
+            s = s[sent:]
+        return None
+
+    def makefile(self, *params):
+        """
+        Return a file-like object associated with this channel.  The optional
+        ``mode`` and ``bufsize`` arguments are interpreted the same way as by
+        the built-in ``file()`` function in Python.
+
+        :return: `.ChannelFile` object which can be used for Python file I/O.
+        """
+        return ChannelFile(*([self] + list(params)))
+
+    def makefile_stderr(self, *params):
+        """
+        Return a file-like object associated with this channel's stderr
+        stream.   Only channels using `exec_command` or `invoke_shell`
+        without a pty will ever have data on the stderr stream.
+
+        The optional ``mode`` and ``bufsize`` arguments are interpreted the
+        same way as by the built-in ``file()`` function in Python.  For a
+        client, it only makes sense to open this file for reading.  For a
+        server, it only makes sense to open this file for writing.
+
+        :returns:
+            `.ChannelStderrFile` object which can be used for Python file I/O.
+
+        .. versionadded:: 1.1
+        """
+        return ChannelStderrFile(*([self] + list(params)))
+
+    def makefile_stdin(self, *params):
+        """
+        Return a file-like object associated with this channel's stdin
+        stream.
+
+        The optional ``mode`` and ``bufsize`` arguments are interpreted the
+        same way as by the built-in ``file()`` function in Python.  For a
+        client, it only makes sense to open this file for writing.  For a
+        server, it only makes sense to open this file for reading.
+
+        :returns:
+            `.ChannelStdinFile` object which can be used for Python file I/O.
+
+        .. versionadded:: 2.6
+        """
+        return ChannelStdinFile(*([self] + list(params)))
+
+    def fileno(self):
+        """
+        Returns an OS-level file descriptor which can be used for polling, but
+        but not for reading or writing.  This is primarily to allow Python's
+        ``select`` module to work.
+
+        The first time ``fileno`` is called on a channel, a pipe is created to
+        simulate real OS-level file descriptor (FD) behavior.  Because of this,
+        two OS-level FDs are created, which will use up FDs faster than normal.
+        (You won't notice this effect unless you have hundreds of channels
+        open at the same time.)
+
+        :return: an OS-level file descriptor (`int`)
+
+        .. warning::
+            This method causes channel reads to be slightly less efficient.
+        """
+        self.lock.acquire()
+        try:
+            if self._pipe is not None:
+                return self._pipe.fileno()
+            # create the pipe and feed in any existing data
+            self._pipe = pipe.make_pipe()
+            p1, p2 = pipe.make_or_pipe(self._pipe)
+            self.in_buffer.set_event(p1)
+            self.in_stderr_buffer.set_event(p2)
+            return self._pipe.fileno()
+        finally:
+            self.lock.release()
+
+    def shutdown(self, how):
+        """
+        Shut down one or both halves of the connection.  If ``how`` is 0,
+        further receives are disallowed.  If ``how`` is 1, further sends
+        are disallowed.  If ``how`` is 2, further sends and receives are
+        disallowed.  This closes the stream in one or both directions.
+
+        :param int how:
+            0 (stop receiving), 1 (stop sending), or 2 (stop receiving and
+              sending).
+        """
+        if (how == 0) or (how == 2):
+            # feign "read" shutdown
+            self.eof_received = 1
+        if (how == 1) or (how == 2):
+            self.lock.acquire()
+            try:
+                m = self._send_eof()
+            finally:
+                self.lock.release()
+            if m is not None:
+                self.transport._send_user_message(m)
+
+    def shutdown_read(self):
+        """
+        Shutdown the receiving side of this socket, closing the stream in
+        the incoming direction.  After this call, future reads on this
+        channel will fail instantly.  This is a convenience method, equivalent
+        to ``shutdown(0)``, for people who don't make it a habit to
+        memorize unix constants from the 1970s.
+
+        .. versionadded:: 1.2
+        """
+        self.shutdown(0)
+
+    def shutdown_write(self):
+        """
+        Shutdown the sending side of this socket, closing the stream in
+        the outgoing direction.  After this call, future writes on this
+        channel will fail instantly.  This is a convenience method, equivalent
+        to ``shutdown(1)``, for people who don't make it a habit to
+        memorize unix constants from the 1970s.
+
+        .. versionadded:: 1.2
+        """
+        self.shutdown(1)
+
+    @property
+    def _closed(self):
+        # Concession to Python 3's socket API, which has a private ._closed
+        # attribute instead of a semipublic .closed attribute.
+        return self.closed
+
+    # ...calls from Transport
+
+    def _set_transport(self, transport):
+        self.transport = transport
+        self.logger = util.get_logger(self.transport.get_log_channel())
+
+    def _set_window(self, window_size, max_packet_size):
+        self.in_window_size = window_size
+        self.in_max_packet_size = max_packet_size
+        # threshold of bytes we receive before we bother to send
+        # a window update
+        self.in_window_threshold = window_size // 10
+        self.in_window_sofar = 0
+        self._log(DEBUG, "Max packet in: {} bytes".format(max_packet_size))
+
+    def _set_remote_channel(self, chanid, window_size, max_packet_size):
+        self.remote_chanid = chanid
+        self.out_window_size = window_size
+        self.out_max_packet_size = self.transport._sanitize_packet_size(
+            max_packet_size
+        )
+        self.active = 1
+        self._log(
+            DEBUG, "Max packet out: {} bytes".format(self.out_max_packet_size)
+        )
+
+    def _request_success(self, m):
+        self._log(DEBUG, "Sesch channel {} request ok".format(self.chanid))
+        self.event_ready = True
+        self.event.set()
+        return
+
+    def _request_failed(self, m):
+        self.lock.acquire()
+        try:
+            msgs = self._close_internal()
+        finally:
+            self.lock.release()
+        for m in msgs:
+            if m is not None:
+                self.transport._send_user_message(m)
+
+    def _feed(self, m):
+        if isinstance(m, bytes):
+            # passed from _feed_extended
+            s = m
+        else:
+            s = m.get_binary()
+        self.in_buffer.feed(s)
+
+    def _feed_extended(self, m):
+        code = m.get_int()
+        s = m.get_binary()
+        if code != 1:
+            self._log(
+                ERROR, "unknown extended_data type {}; discarding".format(code)
+            )
+            return
+        if self.combine_stderr:
+            self._feed(s)
+        else:
+            self.in_stderr_buffer.feed(s)
+
+    def _window_adjust(self, m):
+        nbytes = m.get_int()
+        self.lock.acquire()
+        try:
+            if self.ultra_debug:
+                self._log(DEBUG, "window up {}".format(nbytes))
+            self.out_window_size += nbytes
+            self.out_buffer_cv.notify_all()
+        finally:
+            self.lock.release()
+
+    def _handle_request(self, m):
+        key = m.get_text()
+        want_reply = m.get_boolean()
+        server = self.transport.server_object
+        ok = False
+        if key == "exit-status":
+            self.exit_status = m.get_int()
+            self.status_event.set()
+            ok = True
+        elif key == "xon-xoff":
+            # ignore
+            ok = True
+        elif key == "pty-req":
+            term = m.get_string()
+            width = m.get_int()
+            height = m.get_int()
+            pixelwidth = m.get_int()
+            pixelheight = m.get_int()
+            modes = m.get_string()
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_pty_request(
+                    self, term, width, height, pixelwidth, pixelheight, modes
+                )
+        elif key == "shell":
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_shell_request(self)
+        elif key == "env":
+            name = m.get_string()
+            value = m.get_string()
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_env_request(self, name, value)
+        elif key == "exec":
+            cmd = m.get_string()
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_exec_request(self, cmd)
+        elif key == "subsystem":
+            name = m.get_text()
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_subsystem_request(self, name)
+        elif key == "window-change":
+            width = m.get_int()
+            height = m.get_int()
+            pixelwidth = m.get_int()
+            pixelheight = m.get_int()
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_window_change_request(
+                    self, width, height, pixelwidth, pixelheight
+                )
+        elif key == "x11-req":
+            single_connection = m.get_boolean()
+            auth_proto = m.get_text()
+            auth_cookie = m.get_binary()
+            screen_number = m.get_int()
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_x11_request(
+                    self,
+                    single_connection,
+                    auth_proto,
+                    auth_cookie,
+                    screen_number,
+                )
+        elif key == "auth-agent-req@openssh.com":
+            if server is None:
+                ok = False
+            else:
+                ok = server.check_channel_forward_agent_request(self)
+        else:
+            self._log(DEBUG, 'Unhandled channel request "{}"'.format(key))
+            ok = False
+        if want_reply:
+            m = Message()
+            if ok:
+                m.add_byte(cMSG_CHANNEL_SUCCESS)
+            else:
+                m.add_byte(cMSG_CHANNEL_FAILURE)
+            m.add_int(self.remote_chanid)
+            self.transport._send_user_message(m)
+
+    def _handle_eof(self, m):
+        self.lock.acquire()
+        try:
+            if not self.eof_received:
+                self.eof_received = True
+                self.in_buffer.close()
+                self.in_stderr_buffer.close()
+                if self._pipe is not None:
+                    self._pipe.set_forever()
+        finally:
+            self.lock.release()
+        self._log(DEBUG, "EOF received ({})".format(self._name))
+
+    def _handle_close(self, m):
+        self.lock.acquire()
+        try:
+            msgs = self._close_internal()
+            self.transport._unlink_channel(self.chanid)
+        finally:
+            self.lock.release()
+        for m in msgs:
+            if m is not None:
+                self.transport._send_user_message(m)
+
+    # ...internals...
+
+    def _send(self, s, m):
+        size = len(s)
+        self.lock.acquire()
+        try:
+            if self.closed:
+                # this doesn't seem useful, but it is the documented behavior
+                # of Socket
+                raise socket.error("Socket is closed")
+            size = self._wait_for_send_window(size)
+            if size == 0:
+                # eof or similar
+                return 0
+            m.add_string(s[:size])
+        finally:
+            self.lock.release()
+        # Note: We release self.lock before calling _send_user_message.
+        # Otherwise, we can deadlock during re-keying.
+        self.transport._send_user_message(m)
+        return size
+
+    def _log(self, level, msg, *args):
+        self.logger.log(level, "[chan " + self._name + "] " + msg, *args)
+
+    def _event_pending(self):
+        self.event.clear()
+        self.event_ready = False
+
+    def _wait_for_event(self):
+        self.event.wait()
+        assert self.event.is_set()
+        if self.event_ready:
+            return
+        e = self.transport.get_exception()
+        if e is None:
+            e = SSHException("Channel closed.")
+        raise e
+
+    def _set_closed(self):
+        # you are holding the lock.
+        self.closed = True
+        self.in_buffer.close()
+        self.in_stderr_buffer.close()
+        self.out_buffer_cv.notify_all()
+        # Notify any waiters that we are closed
+        self.event.set()
+        self.status_event.set()
+        if self._pipe is not None:
+            self._pipe.set_forever()
+
+    def _send_eof(self):
+        # you are holding the lock.
+        if self.eof_sent:
+            return None
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_EOF)
+        m.add_int(self.remote_chanid)
+        self.eof_sent = True
+        self._log(DEBUG, "EOF sent ({})".format(self._name))
+        return m
+
+    def _close_internal(self):
+        # you are holding the lock.
+        if not self.active or self.closed:
+            return None, None
+        m1 = self._send_eof()
+        m2 = Message()
+        m2.add_byte(cMSG_CHANNEL_CLOSE)
+        m2.add_int(self.remote_chanid)
+        self._set_closed()
+        # can't unlink from the Transport yet -- the remote side may still
+        # try to send meta-data (exit-status, etc)
+        return m1, m2
+
+    def _unlink(self):
+        # server connection could die before we become active:
+        # still signal the close!
+        if self.closed:
+            return
+        self.lock.acquire()
+        try:
+            self._set_closed()
+            self.transport._unlink_channel(self.chanid)
+        finally:
+            self.lock.release()
+
+    def _check_add_window(self, n):
+        self.lock.acquire()
+        try:
+            if self.closed or self.eof_received or not self.active:
+                return 0
+            if self.ultra_debug:
+                self._log(DEBUG, "addwindow {}".format(n))
+            self.in_window_sofar += n
+            if self.in_window_sofar <= self.in_window_threshold:
+                return 0
+            if self.ultra_debug:
+                self._log(
+                    DEBUG, "addwindow send {}".format(self.in_window_sofar)
+                )
+            out = self.in_window_sofar
+            self.in_window_sofar = 0
+            return out
+        finally:
+            self.lock.release()
+
+    def _wait_for_send_window(self, size):
+        """
+        (You are already holding the lock.)
+        Wait for the send window to open up, and allocate up to ``size`` bytes
+        for transmission.  If no space opens up before the timeout, a timeout
+        exception is raised.  Returns the number of bytes available to send
+        (may be less than requested).
+        """
+        # you are already holding the lock
+        if self.closed or self.eof_sent:
+            return 0
+        if self.out_window_size == 0:
+            # should we block?
+            if self.timeout == 0.0:
+                raise socket.timeout()
+            # loop here in case we get woken up but a different thread has
+            # filled the buffer
+            timeout = self.timeout
+            while self.out_window_size == 0:
+                if self.closed or self.eof_sent:
+                    return 0
+                then = time.time()
+                self.out_buffer_cv.wait(timeout)
+                if timeout is not None:
+                    timeout -= time.time() - then
+                    if timeout <= 0.0:
+                        raise socket.timeout()
+        # we have some window to squeeze into
+        if self.closed or self.eof_sent:
+            return 0
+        if self.out_window_size < size:
+            size = self.out_window_size
+        if self.out_max_packet_size - 64 < size:
+            size = self.out_max_packet_size - 64
+        self.out_window_size -= size
+        if self.ultra_debug:
+            self._log(DEBUG, "window down to {}".format(self.out_window_size))
+        return size
+
+
+class ChannelFile(BufferedFile):
+    """
+    A file-like wrapper around `.Channel`.  A ChannelFile is created by calling
+    `Channel.makefile`.
+
+    .. warning::
+        To correctly emulate the file object created from a socket's `makefile
+        <python:socket.socket.makefile>` method, a `.Channel` and its
+        `.ChannelFile` should be able to be closed or garbage-collected
+        independently. Currently, closing the `ChannelFile` does nothing but
+        flush the buffer.
+    """
+
+    def __init__(self, channel, mode="r", bufsize=-1):
+        self.channel = channel
+        BufferedFile.__init__(self)
+        self._set_mode(mode, bufsize)
+
+    def __repr__(self):
+        """
+        Returns a string representation of this object, for debugging.
+        """
+        return "<paramiko.ChannelFile from " + repr(self.channel) + ">"
+
+    def _read(self, size):
+        return self.channel.recv(size)
+
+    def _write(self, data):
+        self.channel.sendall(data)
+        return len(data)
+
+
+class ChannelStderrFile(ChannelFile):
+    """
+    A file-like wrapper around `.Channel` stderr.
+
+    See `Channel.makefile_stderr` for details.
+    """
+
+    def _read(self, size):
+        return self.channel.recv_stderr(size)
+
+    def _write(self, data):
+        self.channel.sendall_stderr(data)
+        return len(data)
+
+
+class ChannelStdinFile(ChannelFile):
+    """
+    A file-like wrapper around `.Channel` stdin.
+
+    See `Channel.makefile_stdin` for details.
+    """
+
+    def close(self):
+        super().close()
+        self.channel.shutdown_write()
diff --git a/paramiko/client.py b/paramiko/client.py
new file mode 100644
index 0000000..1fe14b0
--- /dev/null
+++ b/paramiko/client.py
@@ -0,0 +1,865 @@
+# Copyright (C) 2006-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+SSH client & key policies
+"""
+
+from binascii import hexlify
+import getpass
+import inspect
+import os
+import socket
+import warnings
+from errno import ECONNREFUSED, EHOSTUNREACH
+
+from paramiko.agent import Agent
+from paramiko.common import DEBUG
+from paramiko.config import SSH_PORT
+from paramiko.dsskey import DSSKey
+from paramiko.ecdsakey import ECDSAKey
+from paramiko.ed25519key import Ed25519Key
+from paramiko.hostkeys import HostKeys
+from paramiko.rsakey import RSAKey
+from paramiko.ssh_exception import (
+    SSHException,
+    BadHostKeyException,
+    NoValidConnectionsError,
+)
+from paramiko.transport import Transport
+from paramiko.util import ClosingContextManager
+
+
+class SSHClient(ClosingContextManager):
+    """
+    A high-level representation of a session with an SSH server.  This class
+    wraps `.Transport`, `.Channel`, and `.SFTPClient` to take care of most
+    aspects of authenticating and opening channels.  A typical use case is::
+
+        client = SSHClient()
+        client.load_system_host_keys()
+        client.connect('ssh.example.com')
+        stdin, stdout, stderr = client.exec_command('ls -l')
+
+    You may pass in explicit overrides for authentication and server host key
+    checking.  The default mechanism is to try to use local key files or an
+    SSH agent (if one is running).
+
+    Instances of this class may be used as context managers.
+
+    .. versionadded:: 1.6
+    """
+
+    def __init__(self):
+        """
+        Create a new SSHClient.
+        """
+        self._system_host_keys = HostKeys()
+        self._host_keys = HostKeys()
+        self._host_keys_filename = None
+        self._log_channel = None
+        self._policy = RejectPolicy()
+        self._transport = None
+        self._agent = None
+
+    def load_system_host_keys(self, filename=None):
+        """
+        Load host keys from a system (read-only) file.  Host keys read with
+        this method will not be saved back by `save_host_keys`.
+
+        This method can be called multiple times.  Each new set of host keys
+        will be merged with the existing set (new replacing old if there are
+        conflicts).
+
+        If ``filename`` is left as ``None``, an attempt will be made to read
+        keys from the user's local "known hosts" file, as used by OpenSSH,
+        and no exception will be raised if the file can't be read.  This is
+        probably only useful on posix.
+
+        :param str filename: the filename to read, or ``None``
+
+        :raises: ``IOError`` --
+            if a filename was provided and the file could not be read
+        """
+        if filename is None:
+            # try the user's .ssh key file, and mask exceptions
+            filename = os.path.expanduser("~/.ssh/known_hosts")
+            try:
+                self._system_host_keys.load(filename)
+            except IOError:
+                pass
+            return
+        self._system_host_keys.load(filename)
+
+    def load_host_keys(self, filename):
+        """
+        Load host keys from a local host-key file.  Host keys read with this
+        method will be checked after keys loaded via `load_system_host_keys`,
+        but will be saved back by `save_host_keys` (so they can be modified).
+        The missing host key policy `.AutoAddPolicy` adds keys to this set and
+        saves them, when connecting to a previously-unknown server.
+
+        This method can be called multiple times.  Each new set of host keys
+        will be merged with the existing set (new replacing old if there are
+        conflicts).  When automatically saving, the last hostname is used.
+
+        :param str filename: the filename to read
+
+        :raises: ``IOError`` -- if the filename could not be read
+        """
+        self._host_keys_filename = filename
+        self._host_keys.load(filename)
+
+    def save_host_keys(self, filename):
+        """
+        Save the host keys back to a file.  Only the host keys loaded with
+        `load_host_keys` (plus any added directly) will be saved -- not any
+        host keys loaded with `load_system_host_keys`.
+
+        :param str filename: the filename to save to
+
+        :raises: ``IOError`` -- if the file could not be written
+        """
+
+        # update local host keys from file (in case other SSH clients
+        # have written to the known_hosts file meanwhile.
+        if self._host_keys_filename is not None:
+            self.load_host_keys(self._host_keys_filename)
+
+        with open(filename, "w") as f:
+            for hostname, keys in self._host_keys.items():
+                for keytype, key in keys.items():
+                    f.write(
+                        "{} {} {}\n".format(
+                            hostname, keytype, key.get_base64()
+                        )
+                    )
+
+    def get_host_keys(self):
+        """
+        Get the local `.HostKeys` object.  This can be used to examine the
+        local host keys or change them.
+
+        :return: the local host keys as a `.HostKeys` object.
+        """
+        return self._host_keys
+
+    def set_log_channel(self, name):
+        """
+        Set the channel for logging.  The default is ``"paramiko.transport"``
+        but it can be set to anything you want.
+
+        :param str name: new channel name for logging
+        """
+        self._log_channel = name
+
+    def set_missing_host_key_policy(self, policy):
+        """
+        Set policy to use when connecting to servers without a known host key.
+
+        Specifically:
+
+        * A **policy** is a "policy class" (or instance thereof), namely some
+          subclass of `.MissingHostKeyPolicy` such as `.RejectPolicy` (the
+          default), `.AutoAddPolicy`, `.WarningPolicy`, or a user-created
+          subclass.
+        * A host key is **known** when it appears in the client object's cached
+          host keys structures (those manipulated by `load_system_host_keys`
+          and/or `load_host_keys`).
+
+        :param .MissingHostKeyPolicy policy:
+            the policy to use when receiving a host key from a
+            previously-unknown server
+        """
+        if inspect.isclass(policy):
+            policy = policy()
+        self._policy = policy
+
+    def _families_and_addresses(self, hostname, port):
+        """
+        Yield pairs of address families and addresses to try for connecting.
+
+        :param str hostname: the server to connect to
+        :param int port: the server port to connect to
+        :returns: Yields an iterable of ``(family, address)`` tuples
+        """
+        guess = True
+        addrinfos = socket.getaddrinfo(
+            hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
+        )
+        for (family, socktype, proto, canonname, sockaddr) in addrinfos:
+            if socktype == socket.SOCK_STREAM:
+                yield family, sockaddr
+                guess = False
+
+        # some OS like AIX don't indicate SOCK_STREAM support, so just
+        # guess. :(  We only do this if we did not get a single result marked
+        # as socktype == SOCK_STREAM.
+        if guess:
+            for family, _, _, _, sockaddr in addrinfos:
+                yield family, sockaddr
+
+    def connect(
+        self,
+        hostname,
+        port=SSH_PORT,
+        username=None,
+        password=None,
+        pkey=None,
+        key_filename=None,
+        timeout=None,
+        allow_agent=True,
+        look_for_keys=True,
+        compress=False,
+        sock=None,
+        gss_auth=False,
+        gss_kex=False,
+        gss_deleg_creds=True,
+        gss_host=None,
+        banner_timeout=None,
+        auth_timeout=None,
+        channel_timeout=None,
+        gss_trust_dns=True,
+        passphrase=None,
+        disabled_algorithms=None,
+        transport_factory=None,
+    ):
+        """
+        Connect to an SSH server and authenticate to it.  The server's host key
+        is checked against the system host keys (see `load_system_host_keys`)
+        and any local host keys (`load_host_keys`).  If the server's hostname
+        is not found in either set of host keys, the missing host key policy
+        is used (see `set_missing_host_key_policy`).  The default policy is
+        to reject the key and raise an `.SSHException`.
+
+        Authentication is attempted in the following order of priority:
+
+            - The ``pkey`` or ``key_filename`` passed in (if any)
+
+              - ``key_filename`` may contain OpenSSH public certificate paths
+                as well as regular private-key paths; when files ending in
+                ``-cert.pub`` are found, they are assumed to match a private
+                key, and both components will be loaded. (The private key
+                itself does *not* need to be listed in ``key_filename`` for
+                this to occur - *just* the certificate.)
+
+            - Any key we can find through an SSH agent
+            - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in
+              ``~/.ssh/``
+
+              - When OpenSSH-style public certificates exist that match an
+                existing such private key (so e.g. one has ``id_rsa`` and
+                ``id_rsa-cert.pub``) the certificate will be loaded alongside
+                the private key and used for authentication.
+
+            - Plain username/password auth, if a password was given
+
+        If a private key requires a password to unlock it, and a password is
+        passed in, that password will be used to attempt to unlock the key.
+
+        :param str hostname: the server to connect to
+        :param int port: the server port to connect to
+        :param str username:
+            the username to authenticate as (defaults to the current local
+            username)
+        :param str password:
+            Used for password authentication; is also used for private key
+            decryption if ``passphrase`` is not given.
+        :param str passphrase:
+            Used for decrypting private keys.
+        :param .PKey pkey: an optional private key to use for authentication
+        :param str key_filename:
+            the filename, or list of filenames, of optional private key(s)
+            and/or certs to try for authentication
+        :param float timeout:
+            an optional timeout (in seconds) for the TCP connect
+        :param bool allow_agent:
+            set to False to disable connecting to the SSH agent
+        :param bool look_for_keys:
+            set to False to disable searching for discoverable private key
+            files in ``~/.ssh/``
+        :param bool compress: set to True to turn on compression
+        :param socket sock:
+            an open socket or socket-like object (such as a `.Channel`) to use
+            for communication to the target host
+        :param bool gss_auth:
+            ``True`` if you want to use GSS-API authentication
+        :param bool gss_kex:
+            Perform GSS-API Key Exchange and user authentication
+        :param bool gss_deleg_creds: Delegate GSS-API client credentials or not
+        :param str gss_host:
+            The targets name in the kerberos database. default: hostname
+        :param bool gss_trust_dns:
+            Indicates whether or not the DNS is trusted to securely
+            canonicalize the name of the host being connected to (default
+            ``True``).
+        :param float banner_timeout: an optional timeout (in seconds) to wait
+            for the SSH banner to be presented.
+        :param float auth_timeout: an optional timeout (in seconds) to wait for
+            an authentication response.
+        :param float channel_timeout: an optional timeout (in seconds) to wait
+             for a channel open response.
+        :param dict disabled_algorithms:
+            an optional dict passed directly to `.Transport` and its keyword
+            argument of the same name.
+        :param transport_factory:
+            an optional callable which is handed a subset of the constructor
+            arguments (primarily those related to the socket, GSS
+            functionality, and algorithm selection) and generates a
+            `.Transport` instance to be used by this client. Defaults to
+            `.Transport.__init__`.
+
+        :raises BadHostKeyException:
+            if the server's host key could not be verified.
+        :raises AuthenticationException: if authentication failed.
+        :raises socket.error:
+            if a socket error (other than connection-refused or
+            host-unreachable) occurred while connecting.
+        :raises NoValidConnectionsError:
+            if all valid connection targets for the requested hostname (eg IPv4
+            and IPv6) yielded connection-refused or host-unreachable socket
+            errors.
+        :raises SSHException:
+            if there was any other error connecting or establishing an SSH
+            session.
+
+        .. versionchanged:: 1.15
+            Added the ``banner_timeout``, ``gss_auth``, ``gss_kex``,
+            ``gss_deleg_creds`` and ``gss_host`` arguments.
+        .. versionchanged:: 2.3
+            Added the ``gss_trust_dns`` argument.
+        .. versionchanged:: 2.4
+            Added the ``passphrase`` argument.
+        .. versionchanged:: 2.6
+            Added the ``disabled_algorithms`` argument.
+        .. versionchanged:: 2.12
+            Added the ``transport_factory`` argument.
+        """
+        if not sock:
+            errors = {}
+            # Try multiple possible address families (e.g. IPv4 vs IPv6)
+            to_try = list(self._families_and_addresses(hostname, port))
+            for af, addr in to_try:
+                try:
+                    sock = socket.socket(af, socket.SOCK_STREAM)
+                    if timeout is not None:
+                        try:
+                            sock.settimeout(timeout)
+                        except:
+                            pass
+                    sock.connect(addr)
+                    # Break out of the loop on success
+                    break
+                except socket.error as e:
+                    # As mentioned in socket docs it is better
+                    # to close sockets explicitly
+                    if sock:
+                        sock.close()
+                    # Raise anything that isn't a straight up connection error
+                    # (such as a resolution error)
+                    if e.errno not in (ECONNREFUSED, EHOSTUNREACH):
+                        raise
+                    # Capture anything else so we know how the run looks once
+                    # iteration is complete. Retain info about which attempt
+                    # this was.
+                    errors[addr] = e
+
+            # Make sure we explode usefully if no address family attempts
+            # succeeded. We've no way of knowing which error is the "right"
+            # one, so we construct a hybrid exception containing all the real
+            # ones, of a subclass that client code should still be watching for
+            # (socket.error)
+            if len(errors) == len(to_try):
+                raise NoValidConnectionsError(errors)
+
+        if transport_factory is None:
+            transport_factory = Transport
+        t = self._transport = transport_factory(
+            sock,
+            gss_kex=gss_kex,
+            gss_deleg_creds=gss_deleg_creds,
+            disabled_algorithms=disabled_algorithms,
+        )
+        t.use_compression(compress=compress)
+        t.set_gss_host(
+            # t.hostname may be None, but GSS-API requires a target name.
+            # Therefore use hostname as fallback.
+            gss_host=gss_host or hostname,
+            trust_dns=gss_trust_dns,
+            gssapi_requested=gss_auth or gss_kex,
+        )
+        if self._log_channel is not None:
+            t.set_log_channel(self._log_channel)
+        if banner_timeout is not None:
+            t.banner_timeout = banner_timeout
+        if auth_timeout is not None:
+            t.auth_timeout = auth_timeout
+        if channel_timeout is not None:
+            t.channel_timeout = channel_timeout
+
+        if port == SSH_PORT:
+            server_hostkey_name = hostname
+        else:
+            server_hostkey_name = "[{}]:{}".format(hostname, port)
+        our_server_keys = None
+
+        our_server_keys = self._system_host_keys.get(server_hostkey_name)
+        if our_server_keys is None:
+            our_server_keys = self._host_keys.get(server_hostkey_name)
+        if our_server_keys is not None:
+            keytype = our_server_keys.keys()[0]
+            sec_opts = t.get_security_options()
+            other_types = [x for x in sec_opts.key_types if x != keytype]
+            sec_opts.key_types = [keytype] + other_types
+
+        t.start_client(timeout=timeout)
+
+        # If GSS-API Key Exchange is performed we are not required to check the
+        # host key, because the host is authenticated via GSS-API / SSPI as
+        # well as our client.
+        if not self._transport.gss_kex_used:
+            server_key = t.get_remote_server_key()
+            if our_server_keys is None:
+                # will raise exception if the key is rejected
+                self._policy.missing_host_key(
+                    self, server_hostkey_name, server_key
+                )
+            else:
+                our_key = our_server_keys.get(server_key.get_name())
+                if our_key != server_key:
+                    if our_key is None:
+                        our_key = list(our_server_keys.values())[0]
+                    raise BadHostKeyException(hostname, server_key, our_key)
+
+        if username is None:
+            username = getpass.getuser()
+
+        if key_filename is None:
+            key_filenames = []
+        elif isinstance(key_filename, str):
+            key_filenames = [key_filename]
+        else:
+            key_filenames = key_filename
+
+        self._auth(
+            username,
+            password,
+            pkey,
+            key_filenames,
+            allow_agent,
+            look_for_keys,
+            gss_auth,
+            gss_kex,
+            gss_deleg_creds,
+            t.gss_host,
+            passphrase,
+        )
+
+    def close(self):
+        """
+        Close this SSHClient and its underlying `.Transport`.
+
+        This should be called anytime you are done using the client object.
+
+        .. warning::
+            Paramiko registers garbage collection hooks that will try to
+            automatically close connections for you, but this is not presently
+            reliable. Failure to explicitly close your client after use may
+            lead to end-of-process hangs!
+        """
+        if self._transport is None:
+            return
+        self._transport.close()
+        self._transport = None
+
+        if self._agent is not None:
+            self._agent.close()
+            self._agent = None
+
+    def exec_command(
+        self,
+        command,
+        bufsize=-1,
+        timeout=None,
+        get_pty=False,
+        environment=None,
+    ):
+        """
+        Execute a command on the SSH server.  A new `.Channel` is opened and
+        the requested command is executed.  The command's input and output
+        streams are returned as Python ``file``-like objects representing
+        stdin, stdout, and stderr.
+
+        :param str command: the command to execute
+        :param int bufsize:
+            interpreted the same way as by the built-in ``file()`` function in
+            Python
+        :param int timeout:
+            set command's channel timeout. See `.Channel.settimeout`
+        :param bool get_pty:
+            Request a pseudo-terminal from the server (default ``False``).
+            See `.Channel.get_pty`
+        :param dict environment:
+            a dict of shell environment variables, to be merged into the
+            default environment that the remote command executes within.
+
+            .. warning::
+                Servers may silently reject some environment variables; see the
+                warning in `.Channel.set_environment_variable` for details.
+
+        :return:
+            the stdin, stdout, and stderr of the executing command, as a
+            3-tuple
+
+        :raises: `.SSHException` -- if the server fails to execute the command
+
+        .. versionchanged:: 1.10
+            Added the ``get_pty`` kwarg.
+        """
+        chan = self._transport.open_session(timeout=timeout)
+        if get_pty:
+            chan.get_pty()
+        chan.settimeout(timeout)
+        if environment:
+            chan.update_environment(environment)
+        chan.exec_command(command)
+        stdin = chan.makefile_stdin("wb", bufsize)
+        stdout = chan.makefile("r", bufsize)
+        stderr = chan.makefile_stderr("r", bufsize)
+        return stdin, stdout, stderr
+
+    def invoke_shell(
+        self,
+        term="vt100",
+        width=80,
+        height=24,
+        width_pixels=0,
+        height_pixels=0,
+        environment=None,
+    ):
+        """
+        Start an interactive shell session on the SSH server.  A new `.Channel`
+        is opened and connected to a pseudo-terminal using the requested
+        terminal type and size.
+
+        :param str term:
+            the terminal type to emulate (for example, ``"vt100"``)
+        :param int width: the width (in characters) of the terminal window
+        :param int height: the height (in characters) of the terminal window
+        :param int width_pixels: the width (in pixels) of the terminal window
+        :param int height_pixels: the height (in pixels) of the terminal window
+        :param dict environment: the command's environment
+        :return: a new `.Channel` connected to the remote shell
+
+        :raises: `.SSHException` -- if the server fails to invoke a shell
+        """
+        chan = self._transport.open_session()
+        chan.get_pty(term, width, height, width_pixels, height_pixels)
+        chan.invoke_shell()
+        return chan
+
+    def open_sftp(self):
+        """
+        Open an SFTP session on the SSH server.
+
+        :return: a new `.SFTPClient` session object
+        """
+        return self._transport.open_sftp_client()
+
+    def get_transport(self):
+        """
+        Return the underlying `.Transport` object for this SSH connection.
+        This can be used to perform lower-level tasks, like opening specific
+        kinds of channels.
+
+        :return: the `.Transport` for this connection
+        """
+        return self._transport
+
+    def _key_from_filepath(self, filename, klass, password):
+        """
+        Attempt to derive a `.PKey` from given string path ``filename``:
+
+        - If ``filename`` appears to be a cert, the matching private key is
+          loaded.
+        - Otherwise, the filename is assumed to be a private key, and the
+          matching public cert will be loaded if it exists.
+        """
+        cert_suffix = "-cert.pub"
+        # Assume privkey, not cert, by default
+        if filename.endswith(cert_suffix):
+            key_path = filename[: -len(cert_suffix)]
+            cert_path = filename
+        else:
+            key_path = filename
+            cert_path = filename + cert_suffix
+        # Blindly try the key path; if no private key, nothing will work.
+        key = klass.from_private_key_file(key_path, password)
+        # TODO: change this to 'Loading' instead of 'Trying' sometime; probably
+        # when #387 is released, since this is a critical log message users are
+        # likely testing/filtering for (bah.)
+        msg = "Trying discovered key {} in {}".format(
+            hexlify(key.get_fingerprint()), key_path
+        )
+        self._log(DEBUG, msg)
+        # Attempt to load cert if it exists.
+        if os.path.isfile(cert_path):
+            key.load_certificate(cert_path)
+            self._log(DEBUG, "Adding public certificate {}".format(cert_path))
+        return key
+
+    def _auth(
+        self,
+        username,
+        password,
+        pkey,
+        key_filenames,
+        allow_agent,
+        look_for_keys,
+        gss_auth,
+        gss_kex,
+        gss_deleg_creds,
+        gss_host,
+        passphrase,
+    ):
+        """
+        Try, in order:
+
+            - The key(s) passed in, if one was passed in.
+            - Any key we can find through an SSH agent (if allowed).
+            - Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in ~/.ssh/
+              (if allowed).
+            - Plain username/password auth, if a password was given.
+
+        (The password might be needed to unlock a private key [if 'passphrase'
+        isn't also given], or for two-factor authentication [for which it is
+        required].)
+        """
+        saved_exception = None
+        two_factor = False
+        allowed_types = set()
+        two_factor_types = {"keyboard-interactive", "password"}
+        if passphrase is None and password is not None:
+            passphrase = password
+
+        # If GSS-API support and GSS-PI Key Exchange was performed, we attempt
+        # authentication with gssapi-keyex.
+        if gss_kex and self._transport.gss_kex_used:
+            try:
+                self._transport.auth_gssapi_keyex(username)
+                return
+            except Exception as e:
+                saved_exception = e
+
+        # Try GSS-API authentication (gssapi-with-mic) only if GSS-API Key
+        # Exchange is not performed, because if we use GSS-API for the key
+        # exchange, there is already a fully established GSS-API context, so
+        # why should we do that again?
+        if gss_auth:
+            try:
+                return self._transport.auth_gssapi_with_mic(
+                    username, gss_host, gss_deleg_creds
+                )
+            except Exception as e:
+                saved_exception = e
+
+        if pkey is not None:
+            try:
+                self._log(
+                    DEBUG,
+                    "Trying SSH key {}".format(
+                        hexlify(pkey.get_fingerprint())
+                    ),
+                )
+                allowed_types = set(
+                    self._transport.auth_publickey(username, pkey)
+                )
+                two_factor = allowed_types & two_factor_types
+                if not two_factor:
+                    return
+            except SSHException as e:
+                saved_exception = e
+
+        if not two_factor:
+            for key_filename in key_filenames:
+                for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):
+                    try:
+                        key = self._key_from_filepath(
+                            key_filename, pkey_class, passphrase
+                        )
+                        allowed_types = set(
+                            self._transport.auth_publickey(username, key)
+                        )
+                        two_factor = allowed_types & two_factor_types
+                        if not two_factor:
+                            return
+                        break
+                    except SSHException as e:
+                        saved_exception = e
+
+        if not two_factor and allow_agent:
+            if self._agent is None:
+                self._agent = Agent()
+
+            for key in self._agent.get_keys():
+                try:
+                    id_ = hexlify(key.get_fingerprint())
+                    self._log(DEBUG, "Trying SSH agent key {}".format(id_))
+                    # for 2-factor auth a successfully auth'd key password
+                    # will return an allowed 2fac auth method
+                    allowed_types = set(
+                        self._transport.auth_publickey(username, key)
+                    )
+                    two_factor = allowed_types & two_factor_types
+                    if not two_factor:
+                        return
+                    break
+                except SSHException as e:
+                    saved_exception = e
+
+        if not two_factor:
+            keyfiles = []
+
+            for keytype, name in [
+                (RSAKey, "rsa"),
+                (DSSKey, "dsa"),
+                (ECDSAKey, "ecdsa"),
+                (Ed25519Key, "ed25519"),
+            ]:
+                # ~/ssh/ is for windows
+                for directory in [".ssh", "ssh"]:
+                    full_path = os.path.expanduser(
+                        "~/{}/id_{}".format(directory, name)
+                    )
+                    if os.path.isfile(full_path):
+                        # TODO: only do this append if below did not run
+                        keyfiles.append((keytype, full_path))
+                        if os.path.isfile(full_path + "-cert.pub"):
+                            keyfiles.append((keytype, full_path + "-cert.pub"))
+
+            if not look_for_keys:
+                keyfiles = []
+
+            for pkey_class, filename in keyfiles:
+                try:
+                    key = self._key_from_filepath(
+                        filename, pkey_class, passphrase
+                    )
+                    # for 2-factor auth a successfully auth'd key will result
+                    # in ['password']
+                    allowed_types = set(
+                        self._transport.auth_publickey(username, key)
+                    )
+                    two_factor = allowed_types & two_factor_types
+                    if not two_factor:
+                        return
+                    break
+                except (SSHException, IOError) as e:
+                    saved_exception = e
+
+        if password is not None:
+            try:
+                self._transport.auth_password(username, password)
+                return
+            except SSHException as e:
+                saved_exception = e
+        elif two_factor:
+            try:
+                self._transport.auth_interactive_dumb(username)
+                return
+            except SSHException as e:
+                saved_exception = e
+
+        # if we got an auth-failed exception earlier, re-raise it
+        if saved_exception is not None:
+            raise saved_exception
+        raise SSHException("No authentication methods available")
+
+    def _log(self, level, msg):
+        self._transport._log(level, msg)
+
+
+class MissingHostKeyPolicy:
+    """
+    Interface for defining the policy that `.SSHClient` should use when the
+    SSH server's hostname is not in either the system host keys or the
+    application's keys.  Pre-made classes implement policies for automatically
+    adding the key to the application's `.HostKeys` object (`.AutoAddPolicy`),
+    and for automatically rejecting the key (`.RejectPolicy`).
+
+    This function may be used to ask the user to verify the key, for example.
+    """
+
+    def missing_host_key(self, client, hostname, key):
+        """
+        Called when an `.SSHClient` receives a server key for a server that
+        isn't in either the system or local `.HostKeys` object.  To accept
+        the key, simply return.  To reject, raised an exception (which will
+        be passed to the calling application).
+        """
+        pass
+
+
+class AutoAddPolicy(MissingHostKeyPolicy):
+    """
+    Policy for automatically adding the hostname and new host key to the
+    local `.HostKeys` object, and saving it.  This is used by `.SSHClient`.
+    """
+
+    def missing_host_key(self, client, hostname, key):
+        client._host_keys.add(hostname, key.get_name(), key)
+        if client._host_keys_filename is not None:
+            client.save_host_keys(client._host_keys_filename)
+        client._log(
+            DEBUG,
+            "Adding {} host key for {}: {}".format(
+                key.get_name(), hostname, hexlify(key.get_fingerprint())
+            ),
+        )
+
+
+class RejectPolicy(MissingHostKeyPolicy):
+    """
+    Policy for automatically rejecting the unknown hostname & key.  This is
+    used by `.SSHClient`.
+    """
+
+    def missing_host_key(self, client, hostname, key):
+        client._log(
+            DEBUG,
+            "Rejecting {} host key for {}: {}".format(
+                key.get_name(), hostname, hexlify(key.get_fingerprint())
+            ),
+        )
+        raise SSHException(
+            "Server {!r} not found in known_hosts".format(hostname)
+        )
+
+
+class WarningPolicy(MissingHostKeyPolicy):
+    """
+    Policy for logging a Python-style warning for an unknown host key, but
+    accepting it. This is used by `.SSHClient`.
+    """
+
+    def missing_host_key(self, client, hostname, key):
+        warnings.warn(
+            "Unknown {} host key for {}: {}".format(
+                key.get_name(), hostname, hexlify(key.get_fingerprint())
+            )
+        )
diff --git a/paramiko/common.py b/paramiko/common.py
new file mode 100644
index 0000000..a9a4e7a
--- /dev/null
+++ b/paramiko/common.py
@@ -0,0 +1,248 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Common constants and global variables.
+"""
+import logging
+import struct
+
+#
+# Formerly of py3compat.py. May be fully delete'able with a deeper look?
+#
+
+
+def byte_chr(c):
+    print("byte_chr")
+    print(c==MSG_SERVICE_REQUEST)
+    print(c)
+    assert isinstance(c, int)
+    return struct.pack("B", c)
+
+
+def byte_mask(c, mask):
+    assert isinstance(c, int)
+    return struct.pack("B", c & mask)
+
+
+def byte_ord(c):
+    # In case we're handed a string instead of an int.
+    if not isinstance(c, int):
+        c = ord(c)
+    return c
+
+
+(
+    MSG_DISCONNECT,
+    MSG_IGNORE,
+    MSG_UNIMPLEMENTED,
+    MSG_DEBUG,
+    MSG_SERVICE_REQUEST,
+    MSG_SERVICE_ACCEPT,
+    MSG_EXT_INFO,
+) = range(1, 8)
+(MSG_KEXINIT, MSG_NEWKEYS) = range(20, 22)
+(
+    MSG_USERAUTH_REQUEST,
+    MSG_USERAUTH_FAILURE,
+    MSG_USERAUTH_SUCCESS,
+    MSG_USERAUTH_BANNER,
+) = range(50, 54)
+MSG_USERAUTH_PK_OK = 60
+(MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE) = range(60, 62)
+(MSG_USERAUTH_GSSAPI_RESPONSE, MSG_USERAUTH_GSSAPI_TOKEN) = range(60, 62)
+(
+    MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
+    MSG_USERAUTH_GSSAPI_ERROR,
+    MSG_USERAUTH_GSSAPI_ERRTOK,
+    MSG_USERAUTH_GSSAPI_MIC,
+) = range(63, 67)
+HIGHEST_USERAUTH_MESSAGE_ID = 79
+(MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE) = range(80, 83)
+(
+    MSG_CHANNEL_OPEN,
+    MSG_CHANNEL_OPEN_SUCCESS,
+    MSG_CHANNEL_OPEN_FAILURE,
+    MSG_CHANNEL_WINDOW_ADJUST,
+    MSG_CHANNEL_DATA,
+    MSG_CHANNEL_EXTENDED_DATA,
+    MSG_CHANNEL_EOF,
+    MSG_CHANNEL_CLOSE,
+    MSG_CHANNEL_REQUEST,
+    MSG_CHANNEL_SUCCESS,
+    MSG_CHANNEL_FAILURE,
+) = range(90, 101)
+
+cMSG_DISCONNECT = byte_chr(MSG_DISCONNECT)
+cMSG_IGNORE = byte_chr(MSG_IGNORE)
+cMSG_UNIMPLEMENTED = byte_chr(MSG_UNIMPLEMENTED)
+cMSG_DEBUG = byte_chr(MSG_DEBUG)
+cMSG_SERVICE_REQUEST = byte_chr(MSG_SERVICE_REQUEST)
+cMSG_SERVICE_ACCEPT = byte_chr(MSG_SERVICE_ACCEPT)
+cMSG_EXT_INFO = byte_chr(MSG_EXT_INFO)
+cMSG_KEXINIT = byte_chr(MSG_KEXINIT)
+cMSG_NEWKEYS = byte_chr(MSG_NEWKEYS)
+cMSG_USERAUTH_REQUEST = byte_chr(MSG_USERAUTH_REQUEST)
+cMSG_USERAUTH_FAILURE = byte_chr(MSG_USERAUTH_FAILURE)
+cMSG_USERAUTH_SUCCESS = byte_chr(MSG_USERAUTH_SUCCESS)
+cMSG_USERAUTH_BANNER = byte_chr(MSG_USERAUTH_BANNER)
+cMSG_USERAUTH_PK_OK = byte_chr(MSG_USERAUTH_PK_OK)
+cMSG_USERAUTH_INFO_REQUEST = byte_chr(MSG_USERAUTH_INFO_REQUEST)
+cMSG_USERAUTH_INFO_RESPONSE = byte_chr(MSG_USERAUTH_INFO_RESPONSE)
+cMSG_USERAUTH_GSSAPI_RESPONSE = byte_chr(MSG_USERAUTH_GSSAPI_RESPONSE)
+cMSG_USERAUTH_GSSAPI_TOKEN = byte_chr(MSG_USERAUTH_GSSAPI_TOKEN)
+cMSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = byte_chr(
+    MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE
+)
+cMSG_USERAUTH_GSSAPI_ERROR = byte_chr(MSG_USERAUTH_GSSAPI_ERROR)
+cMSG_USERAUTH_GSSAPI_ERRTOK = byte_chr(MSG_USERAUTH_GSSAPI_ERRTOK)
+cMSG_USERAUTH_GSSAPI_MIC = byte_chr(MSG_USERAUTH_GSSAPI_MIC)
+cMSG_GLOBAL_REQUEST = byte_chr(MSG_GLOBAL_REQUEST)
+cMSG_REQUEST_SUCCESS = byte_chr(MSG_REQUEST_SUCCESS)
+cMSG_REQUEST_FAILURE = byte_chr(MSG_REQUEST_FAILURE)
+cMSG_CHANNEL_OPEN = byte_chr(MSG_CHANNEL_OPEN)
+cMSG_CHANNEL_OPEN_SUCCESS = byte_chr(MSG_CHANNEL_OPEN_SUCCESS)
+cMSG_CHANNEL_OPEN_FAILURE = byte_chr(MSG_CHANNEL_OPEN_FAILURE)
+cMSG_CHANNEL_WINDOW_ADJUST = byte_chr(MSG_CHANNEL_WINDOW_ADJUST)
+cMSG_CHANNEL_DATA = byte_chr(MSG_CHANNEL_DATA)
+cMSG_CHANNEL_EXTENDED_DATA = byte_chr(MSG_CHANNEL_EXTENDED_DATA)
+cMSG_CHANNEL_EOF = byte_chr(MSG_CHANNEL_EOF)
+cMSG_CHANNEL_CLOSE = byte_chr(MSG_CHANNEL_CLOSE)
+cMSG_CHANNEL_REQUEST = byte_chr(MSG_CHANNEL_REQUEST)
+cMSG_CHANNEL_SUCCESS = byte_chr(MSG_CHANNEL_SUCCESS)
+cMSG_CHANNEL_FAILURE = byte_chr(MSG_CHANNEL_FAILURE)
+
+# for debugging:
+MSG_NAMES = {
+    MSG_DISCONNECT: "disconnect",
+    MSG_IGNORE: "ignore",
+    MSG_UNIMPLEMENTED: "unimplemented",
+    MSG_DEBUG: "debug",
+    MSG_SERVICE_REQUEST: "service-request",
+    MSG_SERVICE_ACCEPT: "service-accept",
+    MSG_KEXINIT: "kexinit",
+    MSG_EXT_INFO: "ext-info",
+    MSG_NEWKEYS: "newkeys",
+    30: "kex30",
+    31: "kex31",
+    32: "kex32",
+    33: "kex33",
+    34: "kex34",
+    40: "kex40",
+    41: "kex41",
+    MSG_USERAUTH_REQUEST: "userauth-request",
+    MSG_USERAUTH_FAILURE: "userauth-failure",
+    MSG_USERAUTH_SUCCESS: "userauth-success",
+    MSG_USERAUTH_BANNER: "userauth--banner",
+    MSG_USERAUTH_PK_OK: "userauth-60(pk-ok/info-request)",
+    MSG_USERAUTH_INFO_RESPONSE: "userauth-info-response",
+    MSG_GLOBAL_REQUEST: "global-request",
+    MSG_REQUEST_SUCCESS: "request-success",
+    MSG_REQUEST_FAILURE: "request-failure",
+    MSG_CHANNEL_OPEN: "channel-open",
+    MSG_CHANNEL_OPEN_SUCCESS: "channel-open-success",
+    MSG_CHANNEL_OPEN_FAILURE: "channel-open-failure",
+    MSG_CHANNEL_WINDOW_ADJUST: "channel-window-adjust",
+    MSG_CHANNEL_DATA: "channel-data",
+    MSG_CHANNEL_EXTENDED_DATA: "channel-extended-data",
+    MSG_CHANNEL_EOF: "channel-eof",
+    MSG_CHANNEL_CLOSE: "channel-close",
+    MSG_CHANNEL_REQUEST: "channel-request",
+    MSG_CHANNEL_SUCCESS: "channel-success",
+    MSG_CHANNEL_FAILURE: "channel-failure",
+    MSG_USERAUTH_GSSAPI_RESPONSE: "userauth-gssapi-response",
+    MSG_USERAUTH_GSSAPI_TOKEN: "userauth-gssapi-token",
+    MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE: "userauth-gssapi-exchange-complete",
+    MSG_USERAUTH_GSSAPI_ERROR: "userauth-gssapi-error",
+    MSG_USERAUTH_GSSAPI_ERRTOK: "userauth-gssapi-error-token",
+    MSG_USERAUTH_GSSAPI_MIC: "userauth-gssapi-mic",
+}
+
+
+# authentication request return codes:
+AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3)
+
+
+# channel request failed reasons:
+(
+    OPEN_SUCCEEDED,
+    OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+    OPEN_FAILED_CONNECT_FAILED,
+    OPEN_FAILED_UNKNOWN_CHANNEL_TYPE,
+    OPEN_FAILED_RESOURCE_SHORTAGE,
+) = range(0, 5)
+
+
+CONNECTION_FAILED_CODE = {
+    1: "Administratively prohibited",
+    2: "Connect failed",
+    3: "Unknown channel type",
+    4: "Resource shortage",
+}
+
+
+(
+    DISCONNECT_SERVICE_NOT_AVAILABLE,
+    DISCONNECT_AUTH_CANCELLED_BY_USER,
+    DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+) = (7, 13, 14)
+
+zero_byte = byte_chr(0)
+one_byte = byte_chr(1)
+four_byte = byte_chr(4)
+max_byte = byte_chr(0xFF)
+cr_byte = byte_chr(13)
+linefeed_byte = byte_chr(10)
+crlf = cr_byte + linefeed_byte
+cr_byte_value = 13
+linefeed_byte_value = 10
+
+
+xffffffff = 0xFFFFFFFF
+x80000000 = 0x80000000
+o666 = 438
+o660 = 432
+o644 = 420
+o600 = 384
+o777 = 511
+o700 = 448
+o70 = 56
+
+DEBUG = logging.DEBUG
+INFO = logging.INFO
+WARNING = logging.WARNING
+ERROR = logging.ERROR
+CRITICAL = logging.CRITICAL
+
+# Common IO/select/etc sleep period, in seconds
+io_sleep = 0.01
+
+DEFAULT_WINDOW_SIZE = 64 * 2**15
+DEFAULT_MAX_PACKET_SIZE = 2**15
+
+# lower bound on the max packet size we'll accept from the remote host
+# Minimum packet size is 32768 bytes according to
+# http://www.ietf.org/rfc/rfc4254.txt
+MIN_WINDOW_SIZE = 2**15
+
+# However, according to http://www.ietf.org/rfc/rfc4253.txt it is perfectly
+# legal to accept a size much smaller, as OpenSSH client does as size 16384.
+MIN_PACKET_SIZE = 2**12
+
+# Max windows size according to http://www.ietf.org/rfc/rfc4254.txt
+MAX_WINDOW_SIZE = 2**32 - 1
diff --git a/paramiko/compress.py b/paramiko/compress.py
new file mode 100644
index 0000000..18ff484
--- /dev/null
+++ b/paramiko/compress.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Compression implementations for a Transport.
+"""
+
+import zlib
+
+
+class ZlibCompressor:
+    def __init__(self):
+        # Use the default level of zlib compression
+        self.z = zlib.compressobj()
+
+    def __call__(self, data):
+        return self.z.compress(data) + self.z.flush(zlib.Z_FULL_FLUSH)
+
+
+class ZlibDecompressor:
+    def __init__(self):
+        self.z = zlib.decompressobj()
+
+    def __call__(self, data):
+        return self.z.decompress(data)
diff --git a/paramiko/config.py b/paramiko/config.py
new file mode 100644
index 0000000..48bcb10
--- /dev/null
+++ b/paramiko/config.py
@@ -0,0 +1,679 @@
+# Copyright (C) 2006-2007  Robey Pointer <robeypointer@gmail.com>
+# Copyright (C) 2012  Olle Lundberg <geek@nerd.sh>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Configuration file (aka ``ssh_config``) support.
+"""
+
+import fnmatch
+import getpass
+import os
+import re
+import shlex
+import socket
+from hashlib import sha1
+from io import StringIO
+from functools import partial
+
+invoke, invoke_import_error = None, None
+try:
+    import invoke
+except ImportError as e:
+    invoke_import_error = e
+
+from .ssh_exception import CouldNotCanonicalize, ConfigParseError
+
+
+SSH_PORT = 22
+
+
+class SSHConfig:
+    """
+    Representation of config information as stored in the format used by
+    OpenSSH. Queries can be made via `lookup`. The format is described in
+    OpenSSH's ``ssh_config`` man page. This class is provided primarily as a
+    convenience to posix users (since the OpenSSH format is a de-facto
+    standard on posix) but should work fine on Windows too.
+
+    .. versionadded:: 1.6
+    """
+
+    SETTINGS_REGEX = re.compile(r"(\w+)(?:\s*=\s*|\s+)(.+)")
+
+    # TODO: do a full scan of ssh.c & friends to make sure we're fully
+    # compatible across the board, e.g. OpenSSH 8.1 added %n to ProxyCommand.
+    TOKENS_BY_CONFIG_KEY = {
+        "controlpath": ["%C", "%h", "%l", "%L", "%n", "%p", "%r", "%u"],
+        "hostname": ["%h"],
+        "identityfile": ["%C", "~", "%d", "%h", "%l", "%u", "%r"],
+        "proxycommand": ["~", "%h", "%p", "%r"],
+        "proxyjump": ["%h", "%p", "%r"],
+        # Doesn't seem worth making this 'special' for now, it will fit well
+        # enough (no actual match-exec config key to be confused with).
+        "match-exec": ["%C", "%d", "%h", "%L", "%l", "%n", "%p", "%r", "%u"],
+    }
+
+    def __init__(self):
+        """
+        Create a new OpenSSH config object.
+
+        Note: the newer alternate constructors `from_path`, `from_file` and
+        `from_text` are simpler to use, as they parse on instantiation. For
+        example, instead of::
+
+            config = SSHConfig()
+            config.parse(open("some-path.config")
+
+        you could::
+
+            config = SSHConfig.from_file(open("some-path.config"))
+            # Or more directly:
+            config = SSHConfig.from_path("some-path.config")
+            # Or if you have arbitrary ssh_config text from some other source:
+            config = SSHConfig.from_text("Host foo\\n\\tUser bar")
+        """
+        self._config = []
+
+    @classmethod
+    def from_text(cls, text):
+        """
+        Create a new, parsed `SSHConfig` from ``text`` string.
+
+        .. versionadded:: 2.7
+        """
+        return cls.from_file(StringIO(text))
+
+    @classmethod
+    def from_path(cls, path):
+        """
+        Create a new, parsed `SSHConfig` from the file found at ``path``.
+
+        .. versionadded:: 2.7
+        """
+        with open(path) as flo:
+            return cls.from_file(flo)
+
+    @classmethod
+    def from_file(cls, flo):
+        """
+        Create a new, parsed `SSHConfig` from file-like object ``flo``.
+
+        .. versionadded:: 2.7
+        """
+        obj = cls()
+        obj.parse(flo)
+        return obj
+
+    def parse(self, file_obj):
+        """
+        Read an OpenSSH config from the given file object.
+
+        :param file_obj: a file-like object to read the config file from
+        """
+        # Start out w/ implicit/anonymous global host-like block to hold
+        # anything not contained by an explicit one.
+        context = {"host": ["*"], "config": {}}
+        for line in file_obj:
+            # Strip any leading or trailing whitespace from the line.
+            # Refer to https://github.com/paramiko/paramiko/issues/499
+            line = line.strip()
+            # Skip blanks, comments
+            if not line or line.startswith("#"):
+                continue
+
+            # Parse line into key, value
+            match = re.match(self.SETTINGS_REGEX, line)
+            if not match:
+                raise ConfigParseError("Unparsable line {}".format(line))
+            key = match.group(1).lower()
+            value = match.group(2)
+
+            # Host keyword triggers switch to new block/context
+            if key in ("host", "match"):
+                self._config.append(context)
+                context = {"config": {}}
+                if key == "host":
+                    # TODO 4.0: make these real objects or at least name this
+                    # "hosts" to acknowledge it's an iterable. (Doing so prior
+                    # to 3.0, despite it being a private API, feels bad -
+                    # surely such an old codebase has folks actually relying on
+                    # these keys.)
+                    context["host"] = self._get_hosts(value)
+                else:
+                    context["matches"] = self._get_matches(value)
+            # Special-case for noop ProxyCommands
+            elif key == "proxycommand" and value.lower() == "none":
+                # Store 'none' as None - not as a string implying that the
+                # proxycommand is the literal shell command "none"!
+                context["config"][key] = None
+            # All other keywords get stored, directly or via append
+            else:
+                if value.startswith('"') and value.endswith('"'):
+                    value = value[1:-1]
+
+                # identityfile, localforward, remoteforward keys are special
+                # cases, since they are allowed to be specified multiple times
+                # and they should be tried in order of specification.
+                if key in ["identityfile", "localforward", "remoteforward"]:
+                    if key in context["config"]:
+                        context["config"][key].append(value)
+                    else:
+                        context["config"][key] = [value]
+                elif key not in context["config"]:
+                    context["config"][key] = value
+        # Store last 'open' block and we're done
+        self._config.append(context)
+
+    def lookup(self, hostname):
+        """
+        Return a dict (`SSHConfigDict`) of config options for a given hostname.
+
+        The host-matching rules of OpenSSH's ``ssh_config`` man page are used:
+        For each parameter, the first obtained value will be used.  The
+        configuration files contain sections separated by ``Host`` and/or
+        ``Match`` specifications, and that section is only applied for hosts
+        which match the given patterns or keywords
+
+        Since the first obtained value for each parameter is used, more host-
+        specific declarations should be given near the beginning of the file,
+        and general defaults at the end.
+
+        The keys in the returned dict are all normalized to lowercase (look for
+        ``"port"``, not ``"Port"``. The values are processed according to the
+        rules for substitution variable expansion in ``ssh_config``.
+
+        Finally, please see the docs for `SSHConfigDict` for deeper info on
+        features such as optional type conversion methods, e.g.::
+
+            conf = my_config.lookup('myhost')
+            assert conf['passwordauthentication'] == 'yes'
+            assert conf.as_bool('passwordauthentication') is True
+
+        .. note::
+            If there is no explicitly configured ``HostName`` value, it will be
+            set to the being-looked-up hostname, which is as close as we can
+            get to OpenSSH's behavior around that particular option.
+
+        :param str hostname: the hostname to lookup
+
+        .. versionchanged:: 2.5
+            Returns `SSHConfigDict` objects instead of dict literals.
+        .. versionchanged:: 2.7
+            Added canonicalization support.
+        .. versionchanged:: 2.7
+            Added ``Match`` support.
+        """
+        # First pass
+        options = self._lookup(hostname=hostname)
+        # Inject HostName if it was not set (this used to be done incidentally
+        # during tokenization, for some reason).
+        if "hostname" not in options:
+            options["hostname"] = hostname
+        # Handle canonicalization
+        canon = options.get("canonicalizehostname", None) in ("yes", "always")
+        maxdots = int(options.get("canonicalizemaxdots", 1))
+        if canon and hostname.count(".") <= maxdots:
+            # NOTE: OpenSSH manpage does not explicitly state this, but its
+            # implementation for CanonicalDomains is 'split on any whitespace'.
+            domains = options["canonicaldomains"].split()
+            hostname = self.canonicalize(hostname, options, domains)
+            # Overwrite HostName again here (this is also what OpenSSH does)
+            options["hostname"] = hostname
+            options = self._lookup(hostname, options, canonical=True)
+        return options
+
+    def _lookup(self, hostname, options=None, canonical=False):
+        # Init
+        if options is None:
+            options = SSHConfigDict()
+        # Iterate all stanzas, applying any that match, in turn (so that things
+        # like Match can reference currently understood state)
+        for context in self._config:
+            if not (
+                self._pattern_matches(context.get("host", []), hostname)
+                or self._does_match(
+                    context.get("matches", []), hostname, canonical, options
+                )
+            ):
+                continue
+            for key, value in context["config"].items():
+                if key not in options:
+                    # Create a copy of the original value,
+                    # else it will reference the original list
+                    # in self._config and update that value too
+                    # when the extend() is being called.
+                    options[key] = value[:] if value is not None else value
+                elif key == "identityfile":
+                    options[key].extend(
+                        x for x in value if x not in options[key]
+                    )
+        # Expand variables in resulting values (besides 'Match exec' which was
+        # already handled above)
+        options = self._expand_variables(options, hostname)
+        return options
+
+    def canonicalize(self, hostname, options, domains):
+        """
+        Return canonicalized version of ``hostname``.
+
+        :param str hostname: Target hostname.
+        :param options: An `SSHConfigDict` from a previous lookup pass.
+        :param domains: List of domains (e.g. ``["paramiko.org"]``).
+
+        :returns: A canonicalized hostname if one was found, else ``None``.
+
+        .. versionadded:: 2.7
+        """
+        found = False
+        for domain in domains:
+            candidate = "{}.{}".format(hostname, domain)
+            family_specific = _addressfamily_host_lookup(candidate, options)
+            if family_specific is not None:
+                # TODO: would we want to dig deeper into other results? e.g. to
+                # find something that satisfies PermittedCNAMEs when that is
+                # implemented?
+                found = family_specific[0]
+            else:
+                # TODO: what does ssh use here and is there a reason to use
+                # that instead of gethostbyname?
+                try:
+                    found = socket.gethostbyname(candidate)
+                except socket.gaierror:
+                    pass
+            if found:
+                # TODO: follow CNAME (implied by found != candidate?) if
+                # CanonicalizePermittedCNAMEs allows it
+                return candidate
+        # If we got here, it means canonicalization failed.
+        # When CanonicalizeFallbackLocal is undefined or 'yes', we just spit
+        # back the original hostname.
+        if options.get("canonicalizefallbacklocal", "yes") == "yes":
+            return hostname
+        # And here, we failed AND fallback was set to a non-yes value, so we
+        # need to get mad.
+        raise CouldNotCanonicalize(hostname)
+
+    def get_hostnames(self):
+        """
+        Return the set of literal hostnames defined in the SSH config (both
+        explicit hostnames and wildcard entries).
+        """
+        hosts = set()
+        for entry in self._config:
+            hosts.update(entry["host"])
+        return hosts
+
+    def _pattern_matches(self, patterns, target):
+        # Convenience auto-splitter if not already a list
+        if hasattr(patterns, "split"):
+            patterns = patterns.split(",")
+        match = False
+        for pattern in patterns:
+            # Short-circuit if target matches a negated pattern
+            if pattern.startswith("!") and fnmatch.fnmatch(
+                target, pattern[1:]
+            ):
+                return False
+            # Flag a match, but continue (in case of later negation) if regular
+            # match occurs
+            elif fnmatch.fnmatch(target, pattern):
+                match = True
+        return match
+
+    def _does_match(self, match_list, target_hostname, canonical, options):
+        matched = []
+        candidates = match_list[:]
+        local_username = getpass.getuser()
+        while candidates:
+            candidate = candidates.pop(0)
+            passed = None
+            # Obtain latest host/user value every loop, so later Match may
+            # reference values assigned within a prior Match.
+            configured_host = options.get("hostname", None)
+            configured_user = options.get("user", None)
+            type_, param = candidate["type"], candidate["param"]
+            # Canonical is a hard pass/fail based on whether this is a
+            # canonicalized re-lookup.
+            if type_ == "canonical":
+                if self._should_fail(canonical, candidate):
+                    return False
+            # The parse step ensures we only see this by itself or after
+            # canonical, so it's also an easy hard pass. (No negation here as
+            # that would be uh, pretty weird?)
+            elif type_ == "all":
+                return True
+            # From here, we are testing various non-hard criteria,
+            # short-circuiting only on fail
+            elif type_ == "host":
+                hostval = configured_host or target_hostname
+                passed = self._pattern_matches(param, hostval)
+            elif type_ == "originalhost":
+                passed = self._pattern_matches(param, target_hostname)
+            elif type_ == "user":
+                user = configured_user or local_username
+                passed = self._pattern_matches(param, user)
+            elif type_ == "localuser":
+                passed = self._pattern_matches(param, local_username)
+            elif type_ == "exec":
+                exec_cmd = self._tokenize(
+                    options, target_hostname, "match-exec", param
+                )
+                # This is the laziest spot in which we can get mad about an
+                # inability to import Invoke.
+                if invoke is None:
+                    raise invoke_import_error
+                # Like OpenSSH, we 'redirect' stdout but let stderr bubble up
+                passed = invoke.run(exec_cmd, hide="stdout", warn=True).ok
+            # Tackle any 'passed, but was negated' results from above
+            if passed is not None and self._should_fail(passed, candidate):
+                return False
+            # Made it all the way here? Everything matched!
+            matched.append(candidate)
+        # Did anything match? (To be treated as bool, usually.)
+        return matched
+
+    def _should_fail(self, would_pass, candidate):
+        return would_pass if candidate["negate"] else not would_pass
+
+    def _tokenize(self, config, target_hostname, key, value):
+        """
+        Tokenize a string based on current config/hostname data.
+
+        :param config: Current config data.
+        :param target_hostname: Original target connection hostname.
+        :param key: Config key being tokenized (used to filter token list).
+        :param value: Config value being tokenized.
+
+        :returns: The tokenized version of the input ``value`` string.
+        """
+        allowed_tokens = self._allowed_tokens(key)
+        # Short-circuit if no tokenization possible
+        if not allowed_tokens:
+            return value
+        # Obtain potentially configured hostname, for use with %h.
+        # Special-case where we are tokenizing the hostname itself, to avoid
+        # replacing %h with a %h-bearing value, etc.
+        configured_hostname = target_hostname
+        if key != "hostname":
+            configured_hostname = config.get("hostname", configured_hostname)
+        # Ditto the rest of the source values
+        if "port" in config:
+            port = config["port"]
+        else:
+            port = SSH_PORT
+        user = getpass.getuser()
+        if "user" in config:
+            remoteuser = config["user"]
+        else:
+            remoteuser = user
+        local_hostname = socket.gethostname().split(".")[0]
+        local_fqdn = LazyFqdn(config, local_hostname)
+        homedir = os.path.expanduser("~")
+        tohash = local_hostname + target_hostname + repr(port) + remoteuser
+        # The actual tokens!
+        replacements = {
+            # TODO: %%???
+            "%C": sha1(tohash.encode()).hexdigest(),
+            "%d": homedir,
+            "%h": configured_hostname,
+            # TODO: %i?
+            "%L": local_hostname,
+            "%l": local_fqdn,
+            # also this is pseudo buggy when not in Match exec mode so document
+            # that. also WHY is that the case?? don't we do all of this late?
+            "%n": target_hostname,
+            "%p": port,
+            "%r": remoteuser,
+            # TODO: %T? don't believe this is possible however
+            "%u": user,
+            "~": homedir,
+        }
+        # Do the thing with the stuff
+        tokenized = value
+        for find, replace in replacements.items():
+            if find not in allowed_tokens:
+                continue
+            tokenized = tokenized.replace(find, str(replace))
+        # TODO: log? eg that value -> tokenized
+        return tokenized
+
+    def _allowed_tokens(self, key):
+        """
+        Given config ``key``, return list of token strings to tokenize.
+
+        .. note::
+            This feels like it wants to eventually go away, but is used to
+            preserve as-strict-as-possible compatibility with OpenSSH, which
+            for whatever reason only applies some tokens to some config keys.
+        """
+        return self.TOKENS_BY_CONFIG_KEY.get(key, [])
+
+    def _expand_variables(self, config, target_hostname):
+        """
+        Return a dict of config options with expanded substitutions
+        for a given original & current target hostname.
+
+        Please refer to :doc:`/api/config` for details.
+
+        :param dict config: the currently parsed config
+        :param str hostname: the hostname whose config is being looked up
+        """
+        for k in config:
+            if config[k] is None:
+                continue
+            tokenizer = partial(self._tokenize, config, target_hostname, k)
+            if isinstance(config[k], list):
+                for i, value in enumerate(config[k]):
+                    config[k][i] = tokenizer(value)
+            else:
+                config[k] = tokenizer(config[k])
+        return config
+
+    def _get_hosts(self, host):
+        """
+        Return a list of host_names from host value.
+        """
+        try:
+            return shlex.split(host)
+        except ValueError:
+            raise ConfigParseError("Unparsable host {}".format(host))
+
+    def _get_matches(self, match):
+        """
+        Parse a specific Match config line into a list-of-dicts for its values.
+
+        Performs some parse-time validation as well.
+        """
+        matches = []
+        tokens = shlex.split(match)
+        while tokens:
+            match = {"type": None, "param": None, "negate": False}
+            type_ = tokens.pop(0)
+            # Handle per-keyword negation
+            if type_.startswith("!"):
+                match["negate"] = True
+                type_ = type_[1:]
+            match["type"] = type_
+            # all/canonical have no params (everything else does)
+            if type_ in ("all", "canonical"):
+                matches.append(match)
+                continue
+            if not tokens:
+                raise ConfigParseError(
+                    "Missing parameter to Match '{}' keyword".format(type_)
+                )
+            match["param"] = tokens.pop(0)
+            matches.append(match)
+        # Perform some (easier to do now than in the middle) validation that is
+        # better handled here than at lookup time.
+        keywords = [x["type"] for x in matches]
+        if "all" in keywords:
+            allowable = ("all", "canonical")
+            ok, bad = (
+                list(filter(lambda x: x in allowable, keywords)),
+                list(filter(lambda x: x not in allowable, keywords)),
+            )
+            err = None
+            if any(bad):
+                err = "Match does not allow 'all' mixed with anything but 'canonical'"  # noqa
+            elif "canonical" in ok and ok.index("canonical") > ok.index("all"):
+                err = "Match does not allow 'all' before 'canonical'"
+            if err is not None:
+                raise ConfigParseError(err)
+        return matches
+
+
+def _addressfamily_host_lookup(hostname, options):
+    """
+    Try looking up ``hostname`` in an IPv4 or IPv6 specific manner.
+
+    This is an odd duck due to needing use in two divergent use cases. It looks
+    up ``AddressFamily`` in ``options`` and if it is ``inet`` or ``inet6``,
+    this function uses `socket.getaddrinfo` to perform a family-specific
+    lookup, returning the result if successful.
+
+    In any other situation -- lookup failure, or ``AddressFamily`` being
+    unspecified or ``any`` -- ``None`` is returned instead and the caller is
+    expected to do something situation-appropriate like calling
+    `socket.gethostbyname`.
+
+    :param str hostname: Hostname to look up.
+    :param options: `SSHConfigDict` instance w/ parsed options.
+    :returns: ``getaddrinfo``-style tuples, or ``None``, depending.
+    """
+    address_family = options.get("addressfamily", "any").lower()
+    if address_family == "any":
+        return
+    try:
+        family = socket.AF_INET6
+        if address_family == "inet":
+            family = socket.AF_INET
+        return socket.getaddrinfo(
+            hostname,
+            None,
+            family,
+            socket.SOCK_DGRAM,
+            socket.IPPROTO_IP,
+            socket.AI_CANONNAME,
+        )
+    except socket.gaierror:
+        pass
+
+
+class LazyFqdn:
+    """
+    Returns the host's fqdn on request as string.
+    """
+
+    def __init__(self, config, host=None):
+        self.fqdn = None
+        self.config = config
+        self.host = host
+
+    def __str__(self):
+        if self.fqdn is None:
+            #
+            # If the SSH config contains AddressFamily, use that when
+            # determining  the local host's FQDN. Using socket.getfqdn() from
+            # the standard library is the most general solution, but can
+            # result in noticeable delays on some platforms when IPv6 is
+            # misconfigured or not available, as it calls getaddrinfo with no
+            # address family specified, so both IPv4 and IPv6 are checked.
+            #
+
+            # Handle specific option
+            fqdn = None
+            results = _addressfamily_host_lookup(self.host, self.config)
+            if results is not None:
+                for res in results:
+                    af, socktype, proto, canonname, sa = res
+                    if canonname and "." in canonname:
+                        fqdn = canonname
+                        break
+            # Handle 'any' / unspecified / lookup failure
+            if fqdn is None:
+                fqdn = socket.getfqdn()
+            # Cache
+            self.fqdn = fqdn
+        return self.fqdn
+
+
+class SSHConfigDict(dict):
+    """
+    A dictionary wrapper/subclass for per-host configuration structures.
+
+    This class introduces some usage niceties for consumers of `SSHConfig`,
+    specifically around the issue of variable type conversions: normal value
+    access yields strings, but there are now methods such as `as_bool` and
+    `as_int` that yield casted values instead.
+
+    For example, given the following ``ssh_config`` file snippet::
+
+        Host foo.example.com
+            PasswordAuthentication no
+            Compression yes
+            ServerAliveInterval 60
+
+    the following code highlights how you can access the raw strings as well as
+    usefully Python type-casted versions (recalling that keys are all
+    normalized to lowercase first)::
+
+        my_config = SSHConfig()
+        my_config.parse(open('~/.ssh/config'))
+        conf = my_config.lookup('foo.example.com')
+
+        assert conf['passwordauthentication'] == 'no'
+        assert conf.as_bool('passwordauthentication') is False
+        assert conf['compression'] == 'yes'
+        assert conf.as_bool('compression') is True
+        assert conf['serveraliveinterval'] == '60'
+        assert conf.as_int('serveraliveinterval') == 60
+
+    .. versionadded:: 2.5
+    """
+
+    def as_bool(self, key):
+        """
+        Express given key's value as a boolean type.
+
+        Typically, this is used for ``ssh_config``'s pseudo-boolean values
+        which are either ``"yes"`` or ``"no"``. In such cases, ``"yes"`` yields
+        ``True`` and any other value becomes ``False``.
+
+        .. note::
+            If (for whatever reason) the stored value is already boolean in
+            nature, it's simply returned.
+
+        .. versionadded:: 2.5
+        """
+        val = self[key]
+        if isinstance(val, bool):
+            return val
+        return val.lower() == "yes"
+
+    def as_int(self, key):
+        """
+        Express given key's value as an integer, if possible.
+
+        This method will raise ``ValueError`` or similar if the value is not
+        int-appropriate, same as the builtin `int` type.
+
+        .. versionadded:: 2.5
+        """
+        return int(self[key])
diff --git a/paramiko/dsskey.py b/paramiko/dsskey.py
new file mode 100644
index 0000000..5a0f85e
--- /dev/null
+++ b/paramiko/dsskey.py
@@ -0,0 +1,255 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+DSS keys.
+"""
+
+from cryptography.exceptions import InvalidSignature
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import dsa
+from cryptography.hazmat.primitives.asymmetric.utils import (
+    decode_dss_signature,
+    encode_dss_signature,
+)
+
+from paramiko import util
+from paramiko.common import zero_byte
+from paramiko.ssh_exception import SSHException
+from paramiko.message import Message
+from paramiko.ber import BER, BERException
+from paramiko.pkey import PKey
+
+
+class DSSKey(PKey):
+    """
+    Representation of a DSS key which can be used to sign an verify SSH2
+    data.
+    """
+
+    def __init__(
+        self,
+        msg=None,
+        data=None,
+        filename=None,
+        password=None,
+        vals=None,
+        file_obj=None,
+    ):
+        self.p = None
+        self.q = None
+        self.g = None
+        self.y = None
+        self.x = None
+        self.public_blob = None
+        if file_obj is not None:
+            self._from_private_key(file_obj, password)
+            return
+        if filename is not None:
+            self._from_private_key_file(filename, password)
+            return
+        if (msg is None) and (data is not None):
+            msg = Message(data)
+        if vals is not None:
+            self.p, self.q, self.g, self.y = vals
+        else:
+            self._check_type_and_load_cert(
+                msg=msg,
+                key_type="ssh-dss",
+                cert_type="ssh-dss-cert-v01@openssh.com",
+            )
+            self.p = msg.get_mpint()
+            self.q = msg.get_mpint()
+            self.g = msg.get_mpint()
+            self.y = msg.get_mpint()
+        self.size = util.bit_length(self.p)
+
+    def asbytes(self):
+        m = Message()
+        m.add_string("ssh-dss")
+        m.add_mpint(self.p)
+        m.add_mpint(self.q)
+        m.add_mpint(self.g)
+        m.add_mpint(self.y)
+        return m.asbytes()
+
+    def __str__(self):
+        return self.asbytes()
+
+    @property
+    def _fields(self):
+        return (self.get_name(), self.p, self.q, self.g, self.y)
+
+    def get_name(self):
+        return "ssh-dss"
+
+    def get_bits(self):
+        return self.size
+
+    def can_sign(self):
+        return self.x is not None
+
+    def sign_ssh_data(self, data, algorithm=None):
+        key = dsa.DSAPrivateNumbers(
+            x=self.x,
+            public_numbers=dsa.DSAPublicNumbers(
+                y=self.y,
+                parameter_numbers=dsa.DSAParameterNumbers(
+                    p=self.p, q=self.q, g=self.g
+                ),
+            ),
+        ).private_key(backend=default_backend())
+        sig = key.sign(data, hashes.SHA1())
+        r, s = decode_dss_signature(sig)
+
+        m = Message()
+        m.add_string("ssh-dss")
+        # apparently, in rare cases, r or s may be shorter than 20 bytes!
+        rstr = util.deflate_long(r, 0)
+        sstr = util.deflate_long(s, 0)
+        if len(rstr) < 20:
+            rstr = zero_byte * (20 - len(rstr)) + rstr
+        if len(sstr) < 20:
+            sstr = zero_byte * (20 - len(sstr)) + sstr
+        m.add_string(rstr + sstr)
+        return m
+
+    def verify_ssh_sig(self, data, msg):
+        if len(msg.asbytes()) == 40:
+            # spies.com bug: signature has no header
+            sig = msg.asbytes()
+        else:
+            kind = msg.get_text()
+            if kind != "ssh-dss":
+                return 0
+            sig = msg.get_binary()
+
+        # pull out (r, s) which are NOT encoded as mpints
+        sigR = util.inflate_long(sig[:20], 1)
+        sigS = util.inflate_long(sig[20:], 1)
+
+        signature = encode_dss_signature(sigR, sigS)
+
+        key = dsa.DSAPublicNumbers(
+            y=self.y,
+            parameter_numbers=dsa.DSAParameterNumbers(
+                p=self.p, q=self.q, g=self.g
+            ),
+        ).public_key(backend=default_backend())
+        try:
+            key.verify(signature, data, hashes.SHA1())
+        except InvalidSignature:
+            return False
+        else:
+            return True
+
+    def write_private_key_file(self, filename, password=None):
+        key = dsa.DSAPrivateNumbers(
+            x=self.x,
+            public_numbers=dsa.DSAPublicNumbers(
+                y=self.y,
+                parameter_numbers=dsa.DSAParameterNumbers(
+                    p=self.p, q=self.q, g=self.g
+                ),
+            ),
+        ).private_key(backend=default_backend())
+
+        self._write_private_key_file(
+            filename,
+            key,
+            serialization.PrivateFormat.TraditionalOpenSSL,
+            password=password,
+        )
+
+    def write_private_key(self, file_obj, password=None):
+        key = dsa.DSAPrivateNumbers(
+            x=self.x,
+            public_numbers=dsa.DSAPublicNumbers(
+                y=self.y,
+                parameter_numbers=dsa.DSAParameterNumbers(
+                    p=self.p, q=self.q, g=self.g
+                ),
+            ),
+        ).private_key(backend=default_backend())
+
+        self._write_private_key(
+            file_obj,
+            key,
+            serialization.PrivateFormat.TraditionalOpenSSL,
+            password=password,
+        )
+
+    @staticmethod
+    def generate(bits=1024, progress_func=None):
+        """
+        Generate a new private DSS key.  This factory function can be used to
+        generate a new host key or authentication key.
+
+        :param int bits: number of bits the generated key should be.
+        :param progress_func: Unused
+        :return: new `.DSSKey` private key
+        """
+        numbers = dsa.generate_private_key(
+            bits, backend=default_backend()
+        ).private_numbers()
+        key = DSSKey(
+            vals=(
+                numbers.public_numbers.parameter_numbers.p,
+                numbers.public_numbers.parameter_numbers.q,
+                numbers.public_numbers.parameter_numbers.g,
+                numbers.public_numbers.y,
+            )
+        )
+        key.x = numbers.x
+        return key
+
+    # ...internals...
+
+    def _from_private_key_file(self, filename, password):
+        data = self._read_private_key_file("DSA", filename, password)
+        self._decode_key(data)
+
+    def _from_private_key(self, file_obj, password):
+        data = self._read_private_key("DSA", file_obj, password)
+        self._decode_key(data)
+
+    def _decode_key(self, data):
+        pkformat, data = data
+        # private key file contains:
+        # DSAPrivateKey = { version = 0, p, q, g, y, x }
+        if pkformat == self._PRIVATE_KEY_FORMAT_ORIGINAL:
+            try:
+                keylist = BER(data).decode()
+            except BERException as e:
+                raise SSHException("Unable to parse key file: {}".format(e))
+        elif pkformat == self._PRIVATE_KEY_FORMAT_OPENSSH:
+            keylist = self._uint32_cstruct_unpack(data, "iiiii")
+            keylist = [0] + list(keylist)
+        else:
+            self._got_bad_key_format_id(pkformat)
+        if type(keylist) is not list or len(keylist) < 6 or keylist[0] != 0:
+            raise SSHException(
+                "not a valid DSA private key file (bad ber encoding)"
+            )
+        self.p = keylist[1]
+        self.q = keylist[2]
+        self.g = keylist[3]
+        self.y = keylist[4]
+        self.x = keylist[5]
+        self.size = util.bit_length(self.p)
diff --git a/paramiko/ecdsakey.py b/paramiko/ecdsakey.py
new file mode 100644
index 0000000..e227975
--- /dev/null
+++ b/paramiko/ecdsakey.py
@@ -0,0 +1,333 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+ECDSA keys
+"""
+
+from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives.asymmetric.utils import (
+    decode_dss_signature,
+    encode_dss_signature,
+)
+
+from paramiko.common import four_byte
+from paramiko.message import Message
+from paramiko.pkey import PKey
+from paramiko.ssh_exception import SSHException
+from paramiko.util import deflate_long
+
+
+class _ECDSACurve:
+    """
+    Represents a specific ECDSA Curve (nistp256, nistp384, etc).
+
+    Handles the generation of the key format identifier and the selection of
+    the proper hash function. Also grabs the proper curve from the 'ecdsa'
+    package.
+    """
+
+    def __init__(self, curve_class, nist_name):
+        self.nist_name = nist_name
+        self.key_length = curve_class.key_size
+
+        # Defined in RFC 5656 6.2
+        self.key_format_identifier = "ecdsa-sha2-" + self.nist_name
+
+        # Defined in RFC 5656 6.2.1
+        if self.key_length <= 256:
+            self.hash_object = hashes.SHA256
+        elif self.key_length <= 384:
+            self.hash_object = hashes.SHA384
+        else:
+            self.hash_object = hashes.SHA512
+
+        self.curve_class = curve_class
+
+
+class _ECDSACurveSet:
+    """
+    A collection to hold the ECDSA curves. Allows querying by oid and by key
+    format identifier. The two ways in which ECDSAKey needs to be able to look
+    up curves.
+    """
+
+    def __init__(self, ecdsa_curves):
+        self.ecdsa_curves = ecdsa_curves
+
+    def get_key_format_identifier_list(self):
+        return [curve.key_format_identifier for curve in self.ecdsa_curves]
+
+    def get_by_curve_class(self, curve_class):
+        for curve in self.ecdsa_curves:
+            if curve.curve_class == curve_class:
+                return curve
+
+    def get_by_key_format_identifier(self, key_format_identifier):
+        for curve in self.ecdsa_curves:
+            if curve.key_format_identifier == key_format_identifier:
+                return curve
+
+    def get_by_key_length(self, key_length):
+        for curve in self.ecdsa_curves:
+            if curve.key_length == key_length:
+                return curve
+
+
+class ECDSAKey(PKey):
+    """
+    Representation of an ECDSA key which can be used to sign and verify SSH2
+    data.
+    """
+
+    _ECDSA_CURVES = _ECDSACurveSet(
+        [
+            _ECDSACurve(ec.SECP256R1, "nistp256"),
+            _ECDSACurve(ec.SECP384R1, "nistp384"),
+            _ECDSACurve(ec.SECP521R1, "nistp521"),
+        ]
+    )
+
+    def __init__(
+        self,
+        msg=None,
+        data=None,
+        filename=None,
+        password=None,
+        vals=None,
+        file_obj=None,
+        validate_point=True,
+    ):
+        self.verifying_key = None
+        self.signing_key = None
+        self.public_blob = None
+        if file_obj is not None:
+            self._from_private_key(file_obj, password)
+            return
+        if filename is not None:
+            self._from_private_key_file(filename, password)
+            return
+        if (msg is None) and (data is not None):
+            msg = Message(data)
+        if vals is not None:
+            self.signing_key, self.verifying_key = vals
+            c_class = self.signing_key.curve.__class__
+            self.ecdsa_curve = self._ECDSA_CURVES.get_by_curve_class(c_class)
+        else:
+            # Must set ecdsa_curve first; subroutines called herein may need to
+            # spit out our get_name(), which relies on this.
+            key_type = msg.get_text()
+            # But this also means we need to hand it a real key/curve
+            # identifier, so strip out any cert business. (NOTE: could push
+            # that into _ECDSACurveSet.get_by_key_format_identifier(), but it
+            # feels more correct to do it here?)
+            suffix = "-cert-v01@openssh.com"
+            if key_type.endswith(suffix):
+                key_type = key_type[: -len(suffix)]
+            self.ecdsa_curve = self._ECDSA_CURVES.get_by_key_format_identifier(
+                key_type
+            )
+            key_types = self._ECDSA_CURVES.get_key_format_identifier_list()
+            cert_types = [
+                "{}-cert-v01@openssh.com".format(x) for x in key_types
+            ]
+            self._check_type_and_load_cert(
+                msg=msg, key_type=key_types, cert_type=cert_types
+            )
+            curvename = msg.get_text()
+            if curvename != self.ecdsa_curve.nist_name:
+                raise SSHException(
+                    "Can't handle curve of type {}".format(curvename)
+                )
+
+            pointinfo = msg.get_binary()
+            try:
+                key = ec.EllipticCurvePublicKey.from_encoded_point(
+                    self.ecdsa_curve.curve_class(), pointinfo
+                )
+                self.verifying_key = key
+            except ValueError:
+                raise SSHException("Invalid public key")
+
+    @classmethod
+    def supported_key_format_identifiers(cls):
+        return cls._ECDSA_CURVES.get_key_format_identifier_list()
+
+    def asbytes(self):
+        key = self.verifying_key
+        m = Message()
+        m.add_string(self.ecdsa_curve.key_format_identifier)
+        m.add_string(self.ecdsa_curve.nist_name)
+
+        numbers = key.public_numbers()
+
+        key_size_bytes = (key.curve.key_size + 7) // 8
+
+        x_bytes = deflate_long(numbers.x, add_sign_padding=False)
+        x_bytes = b"\x00" * (key_size_bytes - len(x_bytes)) + x_bytes
+
+        y_bytes = deflate_long(numbers.y, add_sign_padding=False)
+        y_bytes = b"\x00" * (key_size_bytes - len(y_bytes)) + y_bytes
+
+        point_str = four_byte + x_bytes + y_bytes
+        m.add_string(point_str)
+        return m.asbytes()
+
+    def __str__(self):
+        return self.asbytes()
+
+    @property
+    def _fields(self):
+        return (
+            self.get_name(),
+            self.verifying_key.public_numbers().x,
+            self.verifying_key.public_numbers().y,
+        )
+
+    def get_name(self):
+        return self.ecdsa_curve.key_format_identifier
+
+    def get_bits(self):
+        return self.ecdsa_curve.key_length
+
+    def can_sign(self):
+        return self.signing_key is not None
+
+    def sign_ssh_data(self, data, algorithm=None):
+        ecdsa = ec.ECDSA(self.ecdsa_curve.hash_object())
+        sig = self.signing_key.sign(data, ecdsa)
+        r, s = decode_dss_signature(sig)
+
+        m = Message()
+        m.add_string(self.ecdsa_curve.key_format_identifier)
+        m.add_string(self._sigencode(r, s))
+        return m
+
+    def verify_ssh_sig(self, data, msg):
+        if msg.get_text() != self.ecdsa_curve.key_format_identifier:
+            return False
+        sig = msg.get_binary()
+        sigR, sigS = self._sigdecode(sig)
+        signature = encode_dss_signature(sigR, sigS)
+
+        try:
+            self.verifying_key.verify(
+                signature, data, ec.ECDSA(self.ecdsa_curve.hash_object())
+            )
+        except InvalidSignature:
+            return False
+        else:
+            return True
+
+    def write_private_key_file(self, filename, password=None):
+        self._write_private_key_file(
+            filename,
+            self.signing_key,
+            serialization.PrivateFormat.TraditionalOpenSSL,
+            password=password,
+        )
+
+    def write_private_key(self, file_obj, password=None):
+        self._write_private_key(
+            file_obj,
+            self.signing_key,
+            serialization.PrivateFormat.TraditionalOpenSSL,
+            password=password,
+        )
+
+    @classmethod
+    def generate(cls, curve=ec.SECP256R1(), progress_func=None, bits=None):
+        """
+        Generate a new private ECDSA key.  This factory function can be used to
+        generate a new host key or authentication key.
+
+        :param progress_func: Not used for this type of key.
+        :returns: A new private key (`.ECDSAKey`) object
+        """
+        if bits is not None:
+            curve = cls._ECDSA_CURVES.get_by_key_length(bits)
+            if curve is None:
+                raise ValueError("Unsupported key length: {:d}".format(bits))
+            curve = curve.curve_class()
+
+        private_key = ec.generate_private_key(curve, backend=default_backend())
+        return ECDSAKey(vals=(private_key, private_key.public_key()))
+
+    # ...internals...
+
+    def _from_private_key_file(self, filename, password):
+        data = self._read_private_key_file("EC", filename, password)
+        self._decode_key(data)
+
+    def _from_private_key(self, file_obj, password):
+        data = self._read_private_key("EC", file_obj, password)
+        self._decode_key(data)
+
+    def _decode_key(self, data):
+        pkformat, data = data
+        if pkformat == self._PRIVATE_KEY_FORMAT_ORIGINAL:
+            try:
+                key = serialization.load_der_private_key(
+                    data, password=None, backend=default_backend()
+                )
+            except (
+                ValueError,
+                AssertionError,
+                TypeError,
+                UnsupportedAlgorithm,
+            ) as e:
+                raise SSHException(str(e))
+        elif pkformat == self._PRIVATE_KEY_FORMAT_OPENSSH:
+            try:
+                msg = Message(data)
+                curve_name = msg.get_text()
+                verkey = msg.get_binary()  # noqa: F841
+                sigkey = msg.get_mpint()
+                name = "ecdsa-sha2-" + curve_name
+                curve = self._ECDSA_CURVES.get_by_key_format_identifier(name)
+                if not curve:
+                    raise SSHException("Invalid key curve identifier")
+                key = ec.derive_private_key(
+                    sigkey, curve.curve_class(), default_backend()
+                )
+            except Exception as e:
+                # PKey._read_private_key_openssh() should check or return
+                # keytype - parsing could fail for any reason due to wrong type
+                raise SSHException(str(e))
+        else:
+            self._got_bad_key_format_id(pkformat)
+
+        self.signing_key = key
+        self.verifying_key = key.public_key()
+        curve_class = key.curve.__class__
+        self.ecdsa_curve = self._ECDSA_CURVES.get_by_curve_class(curve_class)
+
+    def _sigencode(self, r, s):
+        msg = Message()
+        msg.add_mpint(r)
+        msg.add_mpint(s)
+        return msg.asbytes()
+
+    def _sigdecode(self, sig):
+        msg = Message(sig)
+        r = msg.get_mpint()
+        s = msg.get_mpint()
+        return r, s
diff --git a/paramiko/ed25519key.py b/paramiko/ed25519key.py
new file mode 100644
index 0000000..c8c4da6
--- /dev/null
+++ b/paramiko/ed25519key.py
@@ -0,0 +1,209 @@
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+import bcrypt
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import Cipher
+
+import nacl.signing
+
+from paramiko.message import Message
+from paramiko.pkey import PKey, OPENSSH_AUTH_MAGIC, _unpad_openssh
+from paramiko.util import b
+from paramiko.ssh_exception import SSHException, PasswordRequiredException
+
+
+class Ed25519Key(PKey):
+    """
+    Representation of an `Ed25519 <https://ed25519.cr.yp.to/>`_ key.
+
+    .. note::
+        Ed25519 key support was added to OpenSSH in version 6.5.
+
+    .. versionadded:: 2.2
+    .. versionchanged:: 2.3
+        Added a ``file_obj`` parameter to match other key classes.
+    """
+
+    def __init__(
+        self, msg=None, data=None, filename=None, password=None, file_obj=None
+    ):
+        self.public_blob = None
+        verifying_key = signing_key = None
+        if msg is None and data is not None:
+            msg = Message(data)
+        if msg is not None:
+            self._check_type_and_load_cert(
+                msg=msg,
+                key_type="ssh-ed25519",
+                cert_type="ssh-ed25519-cert-v01@openssh.com",
+            )
+            verifying_key = nacl.signing.VerifyKey(msg.get_binary())
+        elif filename is not None:
+            with open(filename, "r") as f:
+                pkformat, data = self._read_private_key("OPENSSH", f)
+        elif file_obj is not None:
+            pkformat, data = self._read_private_key("OPENSSH", file_obj)
+
+        if filename or file_obj:
+            signing_key = self._parse_signing_key_data(data, password)
+
+        if signing_key is None and verifying_key is None:
+            raise ValueError("need a key")
+
+        self._signing_key = signing_key
+        self._verifying_key = verifying_key
+
+    def _parse_signing_key_data(self, data, password):
+        from paramiko.transport import Transport
+
+        # We may eventually want this to be usable for other key types, as
+        # OpenSSH moves to it, but for now this is just for Ed25519 keys.
+        # This format is described here:
+        # https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
+        # The description isn't totally complete, and I had to refer to the
+        # source for a full implementation.
+        message = Message(data)
+        if message.get_bytes(len(OPENSSH_AUTH_MAGIC)) != OPENSSH_AUTH_MAGIC:
+            raise SSHException("Invalid key")
+
+        ciphername = message.get_text()
+        kdfname = message.get_text()
+        kdfoptions = message.get_binary()
+        num_keys = message.get_int()
+
+        if kdfname == "none":
+            # kdfname of "none" must have an empty kdfoptions, the ciphername
+            # must be "none"
+            if kdfoptions or ciphername != "none":
+                raise SSHException("Invalid key")
+        elif kdfname == "bcrypt":
+            if not password:
+                raise PasswordRequiredException(
+                    "Private key file is encrypted"
+                )
+            kdf = Message(kdfoptions)
+            bcrypt_salt = kdf.get_binary()
+            bcrypt_rounds = kdf.get_int()
+        else:
+            raise SSHException("Invalid key")
+
+        if ciphername != "none" and ciphername not in Transport._cipher_info:
+            raise SSHException("Invalid key")
+
+        public_keys = []
+        for _ in range(num_keys):
+            pubkey = Message(message.get_binary())
+            if pubkey.get_text() != "ssh-ed25519":
+                raise SSHException("Invalid key")
+            public_keys.append(pubkey.get_binary())
+
+        private_ciphertext = message.get_binary()
+        if ciphername == "none":
+            private_data = private_ciphertext
+        else:
+            cipher = Transport._cipher_info[ciphername]
+            key = bcrypt.kdf(
+                password=b(password),
+                salt=bcrypt_salt,
+                desired_key_bytes=cipher["key-size"] + cipher["block-size"],
+                rounds=bcrypt_rounds,
+                # We can't control how many rounds are on disk, so no sense
+                # warning about it.
+                ignore_few_rounds=True,
+            )
+            decryptor = Cipher(
+                cipher["class"](key[: cipher["key-size"]]),
+                cipher["mode"](key[cipher["key-size"] :]),
+                backend=default_backend(),
+            ).decryptor()
+            private_data = (
+                decryptor.update(private_ciphertext) + decryptor.finalize()
+            )
+
+        message = Message(_unpad_openssh(private_data))
+        if message.get_int() != message.get_int():
+            raise SSHException("Invalid key")
+
+        signing_keys = []
+        for i in range(num_keys):
+            if message.get_text() != "ssh-ed25519":
+                raise SSHException("Invalid key")
+            # A copy of the public key, again, ignore.
+            public = message.get_binary()
+            key_data = message.get_binary()
+            # The second half of the key data is yet another copy of the public
+            # key...
+            signing_key = nacl.signing.SigningKey(key_data[:32])
+            # Verify that all the public keys are the same...
+            assert (
+                signing_key.verify_key.encode()
+                == public
+                == public_keys[i]
+                == key_data[32:]
+            )
+            signing_keys.append(signing_key)
+            # Comment, ignore.
+            message.get_binary()
+
+        if len(signing_keys) != 1:
+            raise SSHException("Invalid key")
+        return signing_keys[0]
+
+    def asbytes(self):
+        if self.can_sign():
+            v = self._signing_key.verify_key
+        else:
+            v = self._verifying_key
+        m = Message()
+        m.add_string("ssh-ed25519")
+        m.add_string(v.encode())
+        return m.asbytes()
+
+    @property
+    def _fields(self):
+        if self.can_sign():
+            v = self._signing_key.verify_key
+        else:
+            v = self._verifying_key
+        return (self.get_name(), v)
+
+    def get_name(self):
+        return "ssh-ed25519"
+
+    def get_bits(self):
+        return 256
+
+    def can_sign(self):
+        return self._signing_key is not None
+
+    def sign_ssh_data(self, data, algorithm=None):
+        m = Message()
+        m.add_string("ssh-ed25519")
+        m.add_string(self._signing_key.sign(data).signature)
+        return m
+
+    def verify_ssh_sig(self, data, msg):
+        if msg.get_text() != "ssh-ed25519":
+            return False
+
+        try:
+            self._verifying_key.verify(data, msg.get_binary())
+        except nacl.exceptions.BadSignatureError:
+            return False
+        else:
+            return True
diff --git a/paramiko/file.py b/paramiko/file.py
new file mode 100644
index 0000000..a36abb9
--- /dev/null
+++ b/paramiko/file.py
@@ -0,0 +1,528 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+from io import BytesIO
+
+from paramiko.common import (
+    linefeed_byte_value,
+    crlf,
+    cr_byte,
+    linefeed_byte,
+    cr_byte_value,
+)
+
+from paramiko.util import ClosingContextManager, u
+
+
+class BufferedFile(ClosingContextManager):
+    """
+    Reusable base class to implement Python-style file buffering around a
+    simpler stream.
+    """
+
+    _DEFAULT_BUFSIZE = 8192
+
+    SEEK_SET = 0
+    SEEK_CUR = 1
+    SEEK_END = 2
+
+    FLAG_READ = 0x1
+    FLAG_WRITE = 0x2
+    FLAG_APPEND = 0x4
+    FLAG_BINARY = 0x10
+    FLAG_BUFFERED = 0x20
+    FLAG_LINE_BUFFERED = 0x40
+    FLAG_UNIVERSAL_NEWLINE = 0x80
+
+    def __init__(self):
+        self.newlines = None
+        self._flags = 0
+        self._bufsize = self._DEFAULT_BUFSIZE
+        self._wbuffer = BytesIO()
+        self._rbuffer = bytes()
+        self._at_trailing_cr = False
+        self._closed = False
+        # pos - position within the file, according to the user
+        # realpos - position according the OS
+        # (these may be different because we buffer for line reading)
+        self._pos = self._realpos = 0
+        # size only matters for seekable files
+        self._size = 0
+
+    def __del__(self):
+        self.close()
+
+    def __iter__(self):
+        """
+        Returns an iterator that can be used to iterate over the lines in this
+        file.  This iterator happens to return the file itself, since a file is
+        its own iterator.
+
+        :raises: ``ValueError`` -- if the file is closed.
+        """
+        if self._closed:
+            raise ValueError("I/O operation on closed file")
+        return self
+
+    def close(self):
+        """
+        Close the file.  Future read and write operations will fail.
+        """
+        self.flush()
+        self._closed = True
+
+    def flush(self):
+        """
+        Write out any data in the write buffer.  This may do nothing if write
+        buffering is not turned on.
+        """
+        self._write_all(self._wbuffer.getvalue())
+        self._wbuffer = BytesIO()
+        return
+
+    def __next__(self):
+        """
+        Returns the next line from the input, or raises ``StopIteration``
+        when EOF is hit.  Unlike python file objects, it's okay to mix
+        calls to `.next` and `.readline`.
+
+        :raises: ``StopIteration`` -- when the end of the file is reached.
+
+        :returns:
+            a line (`str`, or `bytes` if the file was opened in binary mode)
+            read from the file.
+        """
+        line = self.readline()
+        if not line:
+            raise StopIteration
+        return line
+
+    def readable(self):
+        """
+        Check if the file can be read from.
+
+        :returns:
+            `True` if the file can be read from. If `False`, `read` will raise
+            an exception.
+        """
+        return (self._flags & self.FLAG_READ) == self.FLAG_READ
+
+    def writable(self):
+        """
+        Check if the file can be written to.
+
+        :returns:
+            `True` if the file can be written to. If `False`, `write` will
+            raise an exception.
+        """
+        return (self._flags & self.FLAG_WRITE) == self.FLAG_WRITE
+
+    def seekable(self):
+        """
+        Check if the file supports random access.
+
+        :returns:
+            `True` if the file supports random access. If `False`, `seek` will
+            raise an exception.
+        """
+        return False
+
+    def readinto(self, buff):
+        """
+        Read up to ``len(buff)`` bytes into ``bytearray`` *buff* and return the
+        number of bytes read.
+
+        :returns:
+            The number of bytes read.
+        """
+        data = self.read(len(buff))
+        buff[: len(data)] = data
+        return len(data)
+
+    def read(self, size=None):
+        """
+        Read at most ``size`` bytes from the file (less if we hit the end of
+        the file first).  If the ``size`` argument is negative or omitted,
+        read all the remaining data in the file.
+
+        .. note::
+            ``'b'`` mode flag is ignored (``self.FLAG_BINARY`` in
+            ``self._flags``), because SSH treats all files as binary, since we
+            have no idea what encoding the file is in, or even if the file is
+            text data.
+
+        :param int size: maximum number of bytes to read
+        :returns:
+            data read from the file (as bytes), or an empty string if EOF was
+            encountered immediately
+        """
+        if self._closed:
+            raise IOError("File is closed")
+        if not (self._flags & self.FLAG_READ):
+            raise IOError("File is not open for reading")
+        if (size is None) or (size < 0):
+            # go for broke
+            result = bytearray(self._rbuffer)
+            self._rbuffer = bytes()
+            self._pos += len(result)
+            while True:
+                try:
+                    new_data = self._read(self._DEFAULT_BUFSIZE)
+                except EOFError:
+                    new_data = None
+                if (new_data is None) or (len(new_data) == 0):
+                    break
+                result.extend(new_data)
+                self._realpos += len(new_data)
+                self._pos += len(new_data)
+            return bytes(result)
+        if size <= len(self._rbuffer):
+            result = self._rbuffer[:size]
+            self._rbuffer = self._rbuffer[size:]
+            self._pos += len(result)
+            return result
+        while len(self._rbuffer) < size:
+            read_size = size - len(self._rbuffer)
+            if self._flags & self.FLAG_BUFFERED:
+                read_size = max(self._bufsize, read_size)
+            try:
+                new_data = self._read(read_size)
+            except EOFError:
+                new_data = None
+            if (new_data is None) or (len(new_data) == 0):
+                break
+            self._rbuffer += new_data
+            self._realpos += len(new_data)
+        result = self._rbuffer[:size]
+        self._rbuffer = self._rbuffer[size:]
+        self._pos += len(result)
+        return result
+
+    def readline(self, size=None):
+        """
+        Read one entire line from the file.  A trailing newline character is
+        kept in the string (but may be absent when a file ends with an
+        incomplete line).  If the size argument is present and non-negative, it
+        is a maximum byte count (including the trailing newline) and an
+        incomplete line may be returned.  An empty string is returned only when
+        EOF is encountered immediately.
+
+        .. note::
+            Unlike stdio's ``fgets``, the returned string contains null
+            characters (``'\\0'``) if they occurred in the input.
+
+        :param int size: maximum length of returned string.
+        :returns:
+            next line of the file, or an empty string if the end of the
+            file has been reached.
+
+            If the file was opened in binary (``'b'``) mode: bytes are returned
+            Else: the encoding of the file is assumed to be UTF-8 and character
+            strings (`str`) are returned
+        """
+        # it's almost silly how complex this function is.
+        if self._closed:
+            raise IOError("File is closed")
+        if not (self._flags & self.FLAG_READ):
+            raise IOError("File not open for reading")
+        line = self._rbuffer
+        truncated = False
+        while True:
+            if (
+                self._at_trailing_cr
+                and self._flags & self.FLAG_UNIVERSAL_NEWLINE
+                and len(line) > 0
+            ):
+                # edge case: the newline may be '\r\n' and we may have read
+                # only the first '\r' last time.
+                if line[0] == linefeed_byte_value:
+                    line = line[1:]
+                    self._record_newline(crlf)
+                else:
+                    self._record_newline(cr_byte)
+                self._at_trailing_cr = False
+            # check size before looking for a linefeed, in case we already have
+            # enough.
+            if (size is not None) and (size >= 0):
+                if len(line) >= size:
+                    # truncate line
+                    self._rbuffer = line[size:]
+                    line = line[:size]
+                    truncated = True
+                    break
+                n = size - len(line)
+            else:
+                n = self._bufsize
+            if linefeed_byte in line or (
+                self._flags & self.FLAG_UNIVERSAL_NEWLINE and cr_byte in line
+            ):
+                break
+            try:
+                new_data = self._read(n)
+            except EOFError:
+                new_data = None
+            if (new_data is None) or (len(new_data) == 0):
+                self._rbuffer = bytes()
+                self._pos += len(line)
+                return line if self._flags & self.FLAG_BINARY else u(line)
+            line += new_data
+            self._realpos += len(new_data)
+        # find the newline
+        pos = line.find(linefeed_byte)
+        if self._flags & self.FLAG_UNIVERSAL_NEWLINE:
+            rpos = line.find(cr_byte)
+            if (rpos >= 0) and (rpos < pos or pos < 0):
+                pos = rpos
+        if pos == -1:
+            # we couldn't find a newline in the truncated string, return it
+            self._pos += len(line)
+            return line if self._flags & self.FLAG_BINARY else u(line)
+        xpos = pos + 1
+        if (
+            line[pos] == cr_byte_value
+            and xpos < len(line)
+            and line[xpos] == linefeed_byte_value
+        ):
+            xpos += 1
+        # if the string was truncated, _rbuffer needs to have the string after
+        # the newline character plus the truncated part of the line we stored
+        # earlier in _rbuffer
+        if truncated:
+            self._rbuffer = line[xpos:] + self._rbuffer
+        else:
+            self._rbuffer = line[xpos:]
+
+        lf = line[pos:xpos]
+        line = line[:pos] + linefeed_byte
+        if (len(self._rbuffer) == 0) and (lf == cr_byte):
+            # we could read the line up to a '\r' and there could still be a
+            # '\n' following that we read next time.  note that and eat it.
+            self._at_trailing_cr = True
+        else:
+            self._record_newline(lf)
+        self._pos += len(line)
+        return line if self._flags & self.FLAG_BINARY else u(line)
+
+    def readlines(self, sizehint=None):
+        """
+        Read all remaining lines using `readline` and return them as a list.
+        If the optional ``sizehint`` argument is present, instead of reading up
+        to EOF, whole lines totalling approximately sizehint bytes (possibly
+        after rounding up to an internal buffer size) are read.
+
+        :param int sizehint: desired maximum number of bytes to read.
+        :returns: list of lines read from the file.
+        """
+        lines = []
+        byte_count = 0
+        while True:
+            line = self.readline()
+            if len(line) == 0:
+                break
+            lines.append(line)
+            byte_count += len(line)
+            if (sizehint is not None) and (byte_count >= sizehint):
+                break
+        return lines
+
+    def seek(self, offset, whence=0):
+        """
+        Set the file's current position, like stdio's ``fseek``.  Not all file
+        objects support seeking.
+
+        .. note::
+            If a file is opened in append mode (``'a'`` or ``'a+'``), any seek
+            operations will be undone at the next write (as the file position
+            will move back to the end of the file).
+
+        :param int offset:
+            position to move to within the file, relative to ``whence``.
+        :param int whence:
+            type of movement: 0 = absolute; 1 = relative to the current
+            position; 2 = relative to the end of the file.
+
+        :raises: ``IOError`` -- if the file doesn't support random access.
+        """
+        raise IOError("File does not support seeking.")
+
+    def tell(self):
+        """
+        Return the file's current position.  This may not be accurate or
+        useful if the underlying file doesn't support random access, or was
+        opened in append mode.
+
+        :returns: file position (`number <int>` of bytes).
+        """
+        return self._pos
+
+    def write(self, data):
+        """
+        Write data to the file.  If write buffering is on (``bufsize`` was
+        specified and non-zero), some or all of the data may not actually be
+        written yet.  (Use `flush` or `close` to force buffered data to be
+        written out.)
+
+        :param data: ``str``/``bytes`` data to write
+        """
+        if isinstance(data, str):
+            # Accept text and encode as utf-8 for compatibility only.
+            data = data.encode("utf-8")
+        if self._closed:
+            raise IOError("File is closed")
+        if not (self._flags & self.FLAG_WRITE):
+            raise IOError("File not open for writing")
+        if not (self._flags & self.FLAG_BUFFERED):
+            self._write_all(data)
+            return
+        self._wbuffer.write(data)
+        if self._flags & self.FLAG_LINE_BUFFERED:
+            # only scan the new data for linefeed, to avoid wasting time.
+            last_newline_pos = data.rfind(linefeed_byte)
+            if last_newline_pos >= 0:
+                wbuf = self._wbuffer.getvalue()
+                last_newline_pos += len(wbuf) - len(data)
+                self._write_all(wbuf[: last_newline_pos + 1])
+                self._wbuffer = BytesIO()
+                self._wbuffer.write(wbuf[last_newline_pos + 1 :])
+            return
+        # even if we're line buffering, if the buffer has grown past the
+        # buffer size, force a flush.
+        if self._wbuffer.tell() >= self._bufsize:
+            self.flush()
+        return
+
+    def writelines(self, sequence):
+        """
+        Write a sequence of strings to the file.  The sequence can be any
+        iterable object producing strings, typically a list of strings.  (The
+        name is intended to match `readlines`; `writelines` does not add line
+        separators.)
+
+        :param sequence: an iterable sequence of strings.
+        """
+        for line in sequence:
+            self.write(line)
+        return
+
+    def xreadlines(self):
+        """
+        Identical to ``iter(f)``.  This is a deprecated file interface that
+        predates Python iterator support.
+        """
+        return self
+
+    @property
+    def closed(self):
+        return self._closed
+
+    # ...overrides...
+
+    def _read(self, size):
+        """
+        (subclass override)
+        Read data from the stream.  Return ``None`` or raise ``EOFError`` to
+        indicate EOF.
+        """
+        raise EOFError()
+
+    def _write(self, data):
+        """
+        (subclass override)
+        Write data into the stream.
+        """
+        raise IOError("write not implemented")
+
+    def _get_size(self):
+        """
+        (subclass override)
+        Return the size of the file.  This is called from within `_set_mode`
+        if the file is opened in append mode, so the file position can be
+        tracked and `seek` and `tell` will work correctly.  If the file is
+        a stream that can't be randomly accessed, you don't need to override
+        this method,
+        """
+        return 0
+
+    # ...internals...
+
+    def _set_mode(self, mode="r", bufsize=-1):
+        """
+        Subclasses call this method to initialize the BufferedFile.
+        """
+        # set bufsize in any event, because it's used for readline().
+        self._bufsize = self._DEFAULT_BUFSIZE
+        if bufsize < 0:
+            # do no buffering by default, because otherwise writes will get
+            # buffered in a way that will probably confuse people.
+            bufsize = 0
+        if bufsize == 1:
+            # apparently, line buffering only affects writes.  reads are only
+            # buffered if you call readline (directly or indirectly: iterating
+            # over a file will indirectly call readline).
+            self._flags |= self.FLAG_BUFFERED | self.FLAG_LINE_BUFFERED
+        elif bufsize > 1:
+            self._bufsize = bufsize
+            self._flags |= self.FLAG_BUFFERED
+            self._flags &= ~self.FLAG_LINE_BUFFERED
+        elif bufsize == 0:
+            # unbuffered
+            self._flags &= ~(self.FLAG_BUFFERED | self.FLAG_LINE_BUFFERED)
+
+        if ("r" in mode) or ("+" in mode):
+            self._flags |= self.FLAG_READ
+        if ("w" in mode) or ("+" in mode):
+            self._flags |= self.FLAG_WRITE
+        if "a" in mode:
+            self._flags |= self.FLAG_WRITE | self.FLAG_APPEND
+            self._size = self._get_size()
+            self._pos = self._realpos = self._size
+        if "b" in mode:
+            self._flags |= self.FLAG_BINARY
+        if "U" in mode:
+            self._flags |= self.FLAG_UNIVERSAL_NEWLINE
+            # built-in file objects have this attribute to store which kinds of
+            # line terminations they've seen:
+            # <http://www.python.org/doc/current/lib/built-in-funcs.html>
+            self.newlines = None
+
+    def _write_all(self, raw_data):
+        # the underlying stream may be something that does partial writes (like
+        # a socket).
+        data = memoryview(raw_data)
+        while len(data) > 0:
+            count = self._write(data)
+            data = data[count:]
+            if self._flags & self.FLAG_APPEND:
+                self._size += count
+                self._pos = self._realpos = self._size
+            else:
+                self._pos += count
+                self._realpos += count
+        return None
+
+    def _record_newline(self, newline):
+        # silliness about tracking what kinds of newlines we've seen.
+        # i don't understand why it can be None, a string, or a tuple, instead
+        # of just always being a tuple, but we'll emulate that behavior anyway.
+        if not (self._flags & self.FLAG_UNIVERSAL_NEWLINE):
+            return
+        if self.newlines is None:
+            self.newlines = newline
+        elif self.newlines != newline and isinstance(self.newlines, bytes):
+            self.newlines = (self.newlines, newline)
+        elif newline not in self.newlines:
+            self.newlines += (newline,)
diff --git a/paramiko/hostkeys.py b/paramiko/hostkeys.py
new file mode 100644
index 0000000..bbfa575
--- /dev/null
+++ b/paramiko/hostkeys.py
@@ -0,0 +1,389 @@
+# Copyright (C) 2006-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+
+from base64 import encodebytes, decodebytes
+import binascii
+import os
+import re
+
+from collections.abc import MutableMapping
+from hashlib import sha1
+from hmac import HMAC
+
+
+from paramiko.dsskey import DSSKey
+from paramiko.rsakey import RSAKey
+from paramiko.util import get_logger, constant_time_bytes_eq, b, u
+from paramiko.ecdsakey import ECDSAKey
+from paramiko.ed25519key import Ed25519Key
+from paramiko.ssh_exception import SSHException
+
+
+class HostKeys(MutableMapping):
+    """
+    Representation of an OpenSSH-style "known hosts" file.  Host keys can be
+    read from one or more files, and then individual hosts can be looked up to
+    verify server keys during SSH negotiation.
+
+    A `.HostKeys` object can be treated like a dict; any dict lookup is
+    equivalent to calling `lookup`.
+
+    .. versionadded:: 1.5.3
+    """
+
+    def __init__(self, filename=None):
+        """
+        Create a new HostKeys object, optionally loading keys from an OpenSSH
+        style host-key file.
+
+        :param str filename: filename to load host keys from, or ``None``
+        """
+        # emulate a dict of { hostname: { keytype: PKey } }
+        self._entries = []
+        if filename is not None:
+            self.load(filename)
+
+    def add(self, hostname, keytype, key):
+        """
+        Add a host key entry to the table.  Any existing entry for a
+        ``(hostname, keytype)`` pair will be replaced.
+
+        :param str hostname: the hostname (or IP) to add
+        :param str keytype: key type (``"ssh-rsa"`` or ``"ssh-dss"``)
+        :param .PKey key: the key to add
+        """
+        for e in self._entries:
+            if (hostname in e.hostnames) and (e.key.get_name() == keytype):
+                e.key = key
+                return
+        self._entries.append(HostKeyEntry([hostname], key))
+
+    def load(self, filename):
+        """
+        Read a file of known SSH host keys, in the format used by OpenSSH.
+        This type of file unfortunately doesn't exist on Windows, but on
+        posix, it will usually be stored in
+        ``os.path.expanduser("~/.ssh/known_hosts")``.
+
+        If this method is called multiple times, the host keys are merged,
+        not cleared.  So multiple calls to `load` will just call `add`,
+        replacing any existing entries and adding new ones.
+
+        :param str filename: name of the file to read host keys from
+
+        :raises: ``IOError`` -- if there was an error reading the file
+        """
+        with open(filename, "r") as f:
+            for lineno, line in enumerate(f, 1):
+                line = line.strip()
+                if (len(line) == 0) or (line[0] == "#"):
+                    continue
+                try:
+                    e = HostKeyEntry.from_line(line, lineno)
+                except SSHException:
+                    continue
+                if e is not None:
+                    _hostnames = e.hostnames
+                    for h in _hostnames:
+                        if self.check(h, e.key):
+                            e.hostnames.remove(h)
+                    if len(e.hostnames):
+                        self._entries.append(e)
+
+    def save(self, filename):
+        """
+        Save host keys into a file, in the format used by OpenSSH.  The order
+        of keys in the file will be preserved when possible (if these keys were
+        loaded from a file originally).  The single exception is that combined
+        lines will be split into individual key lines, which is arguably a bug.
+
+        :param str filename: name of the file to write
+
+        :raises: ``IOError`` -- if there was an error writing the file
+
+        .. versionadded:: 1.6.1
+        """
+        with open(filename, "w") as f:
+            for e in self._entries:
+                line = e.to_line()
+                if line:
+                    f.write(line)
+
+    def lookup(self, hostname):
+        """
+        Find a hostkey entry for a given hostname or IP.  If no entry is found,
+        ``None`` is returned.  Otherwise a dictionary of keytype to key is
+        returned.  The keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``.
+
+        :param str hostname: the hostname (or IP) to lookup
+        :return: dict of `str` -> `.PKey` keys associated with this host
+            (or ``None``)
+        """
+
+        class SubDict(MutableMapping):
+            def __init__(self, hostname, entries, hostkeys):
+                self._hostname = hostname
+                self._entries = entries
+                self._hostkeys = hostkeys
+
+            def __iter__(self):
+                for k in self.keys():
+                    yield k
+
+            def __len__(self):
+                return len(self.keys())
+
+            def __delitem__(self, key):
+                for e in list(self._entries):
+                    if e.key.get_name() == key:
+                        self._entries.remove(e)
+                        break
+                else:
+                    raise KeyError(key)
+
+            def __getitem__(self, key):
+                for e in self._entries:
+                    if e.key.get_name() == key:
+                        return e.key
+                raise KeyError(key)
+
+            def __setitem__(self, key, val):
+                for e in self._entries:
+                    if e.key is None:
+                        continue
+                    if e.key.get_name() == key:
+                        # replace
+                        e.key = val
+                        break
+                else:
+                    # add a new one
+                    e = HostKeyEntry([hostname], val)
+                    self._entries.append(e)
+                    self._hostkeys._entries.append(e)
+
+            def keys(self):
+                return [
+                    e.key.get_name()
+                    for e in self._entries
+                    if e.key is not None
+                ]
+
+        entries = []
+        for e in self._entries:
+            if self._hostname_matches(hostname, e):
+                entries.append(e)
+        if len(entries) == 0:
+            return None
+        return SubDict(hostname, entries, self)
+
+    def _hostname_matches(self, hostname, entry):
+        """
+        Tests whether ``hostname`` string matches given SubDict ``entry``.
+
+        :returns bool:
+        """
+        for h in entry.hostnames:
+            if (
+                h == hostname
+                or h.startswith("|1|")
+                and not hostname.startswith("|1|")
+                and constant_time_bytes_eq(self.hash_host(hostname, h), h)
+            ):
+                return True
+        return False
+
+    def check(self, hostname, key):
+        """
+        Return True if the given key is associated with the given hostname
+        in this dictionary.
+
+        :param str hostname: hostname (or IP) of the SSH server
+        :param .PKey key: the key to check
+        :return:
+            ``True`` if the key is associated with the hostname; else ``False``
+        """
+        k = self.lookup(hostname)
+        if k is None:
+            return False
+        host_key = k.get(key.get_name(), None)
+        if host_key is None:
+            return False
+        return host_key.asbytes() == key.asbytes()
+
+    def clear(self):
+        """
+        Remove all host keys from the dictionary.
+        """
+        self._entries = []
+
+    def __iter__(self):
+        for k in self.keys():
+            yield k
+
+    def __len__(self):
+        return len(self.keys())
+
+    def __getitem__(self, key):
+        ret = self.lookup(key)
+        if ret is None:
+            raise KeyError(key)
+        return ret
+
+    def __delitem__(self, key):
+        index = None
+        for i, entry in enumerate(self._entries):
+            if self._hostname_matches(key, entry):
+                index = i
+                break
+        if index is None:
+            raise KeyError(key)
+        self._entries.pop(index)
+
+    def __setitem__(self, hostname, entry):
+        # don't use this please.
+        if len(entry) == 0:
+            self._entries.append(HostKeyEntry([hostname], None))
+            return
+        for key_type in entry.keys():
+            found = False
+            for e in self._entries:
+                if (hostname in e.hostnames) and e.key.get_name() == key_type:
+                    # replace
+                    e.key = entry[key_type]
+                    found = True
+            if not found:
+                self._entries.append(HostKeyEntry([hostname], entry[key_type]))
+
+    def keys(self):
+        ret = []
+        for e in self._entries:
+            for h in e.hostnames:
+                if h not in ret:
+                    ret.append(h)
+        return ret
+
+    def values(self):
+        ret = []
+        for k in self.keys():
+            ret.append(self.lookup(k))
+        return ret
+
+    @staticmethod
+    def hash_host(hostname, salt=None):
+        """
+        Return a "hashed" form of the hostname, as used by OpenSSH when storing
+        hashed hostnames in the known_hosts file.
+
+        :param str hostname: the hostname to hash
+        :param str salt: optional salt to use when hashing
+            (must be 20 bytes long)
+        :return: the hashed hostname as a `str`
+        """
+        if salt is None:
+            salt = os.urandom(sha1().digest_size)
+        else:
+            if salt.startswith("|1|"):
+                salt = salt.split("|")[2]
+            salt = decodebytes(b(salt))
+        assert len(salt) == sha1().digest_size
+        hmac = HMAC(salt, b(hostname), sha1).digest()
+        hostkey = "|1|{}|{}".format(u(encodebytes(salt)), u(encodebytes(hmac)))
+        return hostkey.replace("\n", "")
+
+
+class InvalidHostKey(Exception):
+    def __init__(self, line, exc):
+        self.line = line
+        self.exc = exc
+        self.args = (line, exc)
+
+
+class HostKeyEntry:
+    """
+    Representation of a line in an OpenSSH-style "known hosts" file.
+    """
+
+    def __init__(self, hostnames=None, key=None):
+        self.valid = (hostnames is not None) and (key is not None)
+        self.hostnames = hostnames
+        self.key = key
+
+    @classmethod
+    def from_line(cls, line, lineno=None):
+        """
+        Parses the given line of text to find the names for the host,
+        the type of key, and the key data. The line is expected to be in the
+        format used by the OpenSSH known_hosts file. Fields are separated by a
+        single space or tab.
+
+        Lines are expected to not have leading or trailing whitespace.
+        We don't bother to check for comments or empty lines.  All of
+        that should be taken care of before sending the line to us.
+
+        :param str line: a line from an OpenSSH known_hosts file
+        """
+        log = get_logger("paramiko.hostkeys")
+        fields = re.split(" |\t", line)
+        if len(fields) < 3:
+            # Bad number of fields
+            msg = "Not enough fields found in known_hosts in line {} ({!r})"
+            log.info(msg.format(lineno, line))
+            return None
+        fields = fields[:3]
+
+        names, keytype, key = fields
+        names = names.split(",")
+
+        # Decide what kind of key we're looking at and create an object
+        # to hold it accordingly.
+        try:
+            key = b(key)
+            if keytype == "ssh-rsa":
+                key = RSAKey(data=decodebytes(key))
+            elif keytype == "ssh-dss":
+                key = DSSKey(data=decodebytes(key))
+            elif keytype in ECDSAKey.supported_key_format_identifiers():
+                key = ECDSAKey(data=decodebytes(key), validate_point=False)
+            elif keytype == "ssh-ed25519":
+                key = Ed25519Key(data=decodebytes(key))
+            else:
+                log.info("Unable to handle key of type {}".format(keytype))
+                return None
+
+        except binascii.Error as e:
+            raise InvalidHostKey(line, e)
+
+        return cls(names, key)
+
+    def to_line(self):
+        """
+        Returns a string in OpenSSH known_hosts file format, or None if
+        the object is not in a valid state.  A trailing newline is
+        included.
+        """
+        if self.valid:
+            return "{} {} {}\n".format(
+                ",".join(self.hostnames),
+                self.key.get_name(),
+                self.key.get_base64(),
+            )
+        return None
+
+    def __repr__(self):
+        return "<HostKeyEntry {!r}: {!r}>".format(self.hostnames, self.key)
diff --git a/paramiko/kex_curve25519.py b/paramiko/kex_curve25519.py
new file mode 100644
index 0000000..20c23e4
--- /dev/null
+++ b/paramiko/kex_curve25519.py
@@ -0,0 +1,131 @@
+import binascii
+import hashlib
+
+from cryptography.exceptions import UnsupportedAlgorithm
+from cryptography.hazmat.primitives import constant_time, serialization
+from cryptography.hazmat.primitives.asymmetric.x25519 import (
+    X25519PrivateKey,
+    X25519PublicKey,
+)
+
+from paramiko.message import Message
+from paramiko.common import byte_chr
+from paramiko.ssh_exception import SSHException
+
+
+_MSG_KEXECDH_INIT, _MSG_KEXECDH_REPLY = range(30, 32)
+c_MSG_KEXECDH_INIT, c_MSG_KEXECDH_REPLY = [byte_chr(c) for c in range(30, 32)]
+
+
+class KexCurve25519:
+    hash_algo = hashlib.sha256
+
+    def __init__(self, transport):
+        self.transport = transport
+        self.key = None
+
+    @classmethod
+    def is_available(cls):
+        try:
+            X25519PrivateKey.generate()
+        except UnsupportedAlgorithm:
+            return False
+        else:
+            return True
+
+    def _perform_exchange(self, peer_key):
+        secret = self.key.exchange(peer_key)
+        if constant_time.bytes_eq(secret, b"\x00" * 32):
+            raise SSHException(
+                "peer's curve25519 public value has wrong order"
+            )
+        return secret
+
+    def start_kex(self):
+        self.key = X25519PrivateKey.generate()
+        if self.transport.server_mode:
+            self.transport._expect_packet(_MSG_KEXECDH_INIT)
+            return
+
+        m = Message()
+        m.add_byte(c_MSG_KEXECDH_INIT)
+        m.add_string(
+            self.key.public_key().public_bytes(
+                serialization.Encoding.Raw, serialization.PublicFormat.Raw
+            )
+        )
+        self.transport._send_message(m)
+        self.transport._expect_packet(_MSG_KEXECDH_REPLY)
+
+    def parse_next(self, ptype, m):
+        if self.transport.server_mode and (ptype == _MSG_KEXECDH_INIT):
+            return self._parse_kexecdh_init(m)
+        elif not self.transport.server_mode and (ptype == _MSG_KEXECDH_REPLY):
+            return self._parse_kexecdh_reply(m)
+        raise SSHException(
+            "KexCurve25519 asked to handle packet type {:d}".format(ptype)
+        )
+
+    def _parse_kexecdh_init(self, m):
+        peer_key_bytes = m.get_string()
+        peer_key = X25519PublicKey.from_public_bytes(peer_key_bytes)
+        K = self._perform_exchange(peer_key)
+        K = int(binascii.hexlify(K), 16)
+        # compute exchange hash
+        hm = Message()
+        hm.add(
+            self.transport.remote_version,
+            self.transport.local_version,
+            self.transport.remote_kex_init,
+            self.transport.local_kex_init,
+        )
+        server_key_bytes = self.transport.get_server_key().asbytes()
+        exchange_key_bytes = self.key.public_key().public_bytes(
+            serialization.Encoding.Raw, serialization.PublicFormat.Raw
+        )
+        hm.add_string(server_key_bytes)
+        hm.add_string(peer_key_bytes)
+        hm.add_string(exchange_key_bytes)
+        hm.add_mpint(K)
+        H = self.hash_algo(hm.asbytes()).digest()
+        self.transport._set_K_H(K, H)
+        sig = self.transport.get_server_key().sign_ssh_data(
+            H, self.transport.host_key_type
+        )
+        # construct reply
+        m = Message()
+        m.add_byte(c_MSG_KEXECDH_REPLY)
+        m.add_string(server_key_bytes)
+        m.add_string(exchange_key_bytes)
+        m.add_string(sig)
+        self.transport._send_message(m)
+        self.transport._activate_outbound()
+
+    def _parse_kexecdh_reply(self, m):
+        peer_host_key_bytes = m.get_string()
+        peer_key_bytes = m.get_string()
+        sig = m.get_binary()
+
+        peer_key = X25519PublicKey.from_public_bytes(peer_key_bytes)
+
+        K = self._perform_exchange(peer_key)
+        K = int(binascii.hexlify(K), 16)
+        # compute exchange hash and verify signature
+        hm = Message()
+        hm.add(
+            self.transport.local_version,
+            self.transport.remote_version,
+            self.transport.local_kex_init,
+            self.transport.remote_kex_init,
+        )
+        hm.add_string(peer_host_key_bytes)
+        hm.add_string(
+            self.key.public_key().public_bytes(
+                serialization.Encoding.Raw, serialization.PublicFormat.Raw
+            )
+        )
+        hm.add_string(peer_key_bytes)
+        hm.add_mpint(K)
+        self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
+        self.transport._verify_key(peer_host_key_bytes, sig)
+        self.transport._activate_outbound()
diff --git a/paramiko/kex_ecdh_nist.py b/paramiko/kex_ecdh_nist.py
new file mode 100644
index 0000000..41fab46
--- /dev/null
+++ b/paramiko/kex_ecdh_nist.py
@@ -0,0 +1,151 @@
+"""
+Ephemeral Elliptic Curve Diffie-Hellman (ECDH) key exchange
+RFC 5656, Section 4
+"""
+
+from hashlib import sha256, sha384, sha512
+from paramiko.common import byte_chr
+from paramiko.message import Message
+from paramiko.ssh_exception import SSHException
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives import serialization
+from binascii import hexlify
+
+_MSG_KEXECDH_INIT, _MSG_KEXECDH_REPLY = range(30, 32)
+c_MSG_KEXECDH_INIT, c_MSG_KEXECDH_REPLY = [byte_chr(c) for c in range(30, 32)]
+
+
+class KexNistp256:
+
+    name = "ecdh-sha2-nistp256"
+    hash_algo = sha256
+    curve = ec.SECP256R1()
+
+    def __init__(self, transport):
+        self.transport = transport
+        # private key, client public and server public keys
+        self.P = 0
+        self.Q_C = None
+        self.Q_S = None
+
+    def start_kex(self):
+        self._generate_key_pair()
+        if self.transport.server_mode:
+            self.transport._expect_packet(_MSG_KEXECDH_INIT)
+            return
+        m = Message()
+        m.add_byte(c_MSG_KEXECDH_INIT)
+        # SEC1: V2.0  2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
+        m.add_string(
+            self.Q_C.public_bytes(
+                serialization.Encoding.X962,
+                serialization.PublicFormat.UncompressedPoint,
+            )
+        )
+        self.transport._send_message(m)
+        self.transport._expect_packet(_MSG_KEXECDH_REPLY)
+
+    def parse_next(self, ptype, m):
+        if self.transport.server_mode and (ptype == _MSG_KEXECDH_INIT):
+            return self._parse_kexecdh_init(m)
+        elif not self.transport.server_mode and (ptype == _MSG_KEXECDH_REPLY):
+            return self._parse_kexecdh_reply(m)
+        raise SSHException(
+            "KexECDH asked to handle packet type {:d}".format(ptype)
+        )
+
+    def _generate_key_pair(self):
+        self.P = ec.generate_private_key(self.curve, default_backend())
+        if self.transport.server_mode:
+            self.Q_S = self.P.public_key()
+            return
+        self.Q_C = self.P.public_key()
+
+    def _parse_kexecdh_init(self, m):
+        Q_C_bytes = m.get_string()
+        self.Q_C = ec.EllipticCurvePublicKey.from_encoded_point(
+            self.curve, Q_C_bytes
+        )
+        K_S = self.transport.get_server_key().asbytes()
+        K = self.P.exchange(ec.ECDH(), self.Q_C)
+        K = int(hexlify(K), 16)
+        # compute exchange hash
+        hm = Message()
+        hm.add(
+            self.transport.remote_version,
+            self.transport.local_version,
+            self.transport.remote_kex_init,
+            self.transport.local_kex_init,
+        )
+        hm.add_string(K_S)
+        hm.add_string(Q_C_bytes)
+        # SEC1: V2.0  2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
+        hm.add_string(
+            self.Q_S.public_bytes(
+                serialization.Encoding.X962,
+                serialization.PublicFormat.UncompressedPoint,
+            )
+        )
+        hm.add_mpint(int(K))
+        H = self.hash_algo(hm.asbytes()).digest()
+        self.transport._set_K_H(K, H)
+        sig = self.transport.get_server_key().sign_ssh_data(
+            H, self.transport.host_key_type
+        )
+        # construct reply
+        m = Message()
+        m.add_byte(c_MSG_KEXECDH_REPLY)
+        m.add_string(K_S)
+        m.add_string(
+            self.Q_S.public_bytes(
+                serialization.Encoding.X962,
+                serialization.PublicFormat.UncompressedPoint,
+            )
+        )
+        m.add_string(sig)
+        self.transport._send_message(m)
+        self.transport._activate_outbound()
+
+    def _parse_kexecdh_reply(self, m):
+        K_S = m.get_string()
+        Q_S_bytes = m.get_string()
+        self.Q_S = ec.EllipticCurvePublicKey.from_encoded_point(
+            self.curve, Q_S_bytes
+        )
+        sig = m.get_binary()
+        K = self.P.exchange(ec.ECDH(), self.Q_S)
+        K = int(hexlify(K), 16)
+        # compute exchange hash and verify signature
+        hm = Message()
+        hm.add(
+            self.transport.local_version,
+            self.transport.remote_version,
+            self.transport.local_kex_init,
+            self.transport.remote_kex_init,
+        )
+        hm.add_string(K_S)
+        # SEC1: V2.0  2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
+        hm.add_string(
+            self.Q_C.public_bytes(
+                serialization.Encoding.X962,
+                serialization.PublicFormat.UncompressedPoint,
+            )
+        )
+        hm.add_string(Q_S_bytes)
+        hm.add_mpint(K)
+        self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
+        self.transport._verify_key(K_S, sig)
+        self.transport._activate_outbound()
+
+
+class KexNistp384(KexNistp256):
+    name = "ecdh-sha2-nistp384"
+    hash_algo = sha384
+    curve = ec.SECP384R1()
+
+
+class KexNistp521(KexNistp256):
+    name = "ecdh-sha2-nistp521"
+    hash_algo = sha512
+    curve = ec.SECP521R1()
diff --git a/paramiko/kex_gex.py b/paramiko/kex_gex.py
new file mode 100644
index 0000000..baa0803
--- /dev/null
+++ b/paramiko/kex_gex.py
@@ -0,0 +1,288 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Variant on `KexGroup1 <paramiko.kex_group1.KexGroup1>` where the prime "p" and
+generator "g" are provided by the server.  A bit more work is required on the
+client side, and a **lot** more on the server side.
+"""
+
+import os
+from hashlib import sha1, sha256
+
+from paramiko import util
+from paramiko.common import DEBUG, byte_chr, byte_ord, byte_mask
+from paramiko.message import Message
+from paramiko.ssh_exception import SSHException
+
+
+(
+    _MSG_KEXDH_GEX_REQUEST_OLD,
+    _MSG_KEXDH_GEX_GROUP,
+    _MSG_KEXDH_GEX_INIT,
+    _MSG_KEXDH_GEX_REPLY,
+    _MSG_KEXDH_GEX_REQUEST,
+) = range(30, 35)
+
+(
+    c_MSG_KEXDH_GEX_REQUEST_OLD,
+    c_MSG_KEXDH_GEX_GROUP,
+    c_MSG_KEXDH_GEX_INIT,
+    c_MSG_KEXDH_GEX_REPLY,
+    c_MSG_KEXDH_GEX_REQUEST,
+) = [byte_chr(c) for c in range(30, 35)]
+
+
+class KexGex:
+
+    name = "diffie-hellman-group-exchange-sha1"
+    min_bits = 1024
+    max_bits = 8192
+    preferred_bits = 2048
+    hash_algo = sha1
+
+    def __init__(self, transport):
+        self.transport = transport
+        self.p = None
+        self.q = None
+        self.g = None
+        self.x = None
+        self.e = None
+        self.f = None
+        self.old_style = False
+
+    def start_kex(self, _test_old_style=False):
+        if self.transport.server_mode:
+            self.transport._expect_packet(
+                _MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD
+            )
+            return
+        # request a bit range: we accept (min_bits) to (max_bits), but prefer
+        # (preferred_bits).  according to the spec, we shouldn't pull the
+        # minimum up above 1024.
+        m = Message()
+        if _test_old_style:
+            # only used for unit tests: we shouldn't ever send this
+            m.add_byte(c_MSG_KEXDH_GEX_REQUEST_OLD)
+            m.add_int(self.preferred_bits)
+            self.old_style = True
+        else:
+            m.add_byte(c_MSG_KEXDH_GEX_REQUEST)
+            m.add_int(self.min_bits)
+            m.add_int(self.preferred_bits)
+            m.add_int(self.max_bits)
+        self.transport._send_message(m)
+        self.transport._expect_packet(_MSG_KEXDH_GEX_GROUP)
+
+    def parse_next(self, ptype, m):
+        if ptype == _MSG_KEXDH_GEX_REQUEST:
+            return self._parse_kexdh_gex_request(m)
+        elif ptype == _MSG_KEXDH_GEX_GROUP:
+            return self._parse_kexdh_gex_group(m)
+        elif ptype == _MSG_KEXDH_GEX_INIT:
+            return self._parse_kexdh_gex_init(m)
+        elif ptype == _MSG_KEXDH_GEX_REPLY:
+            return self._parse_kexdh_gex_reply(m)
+        elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD:
+            return self._parse_kexdh_gex_request_old(m)
+        msg = "KexGex {} asked to handle packet type {:d}"
+        raise SSHException(msg.format(self.name, ptype))
+
+    # ...internals...
+
+    def _generate_x(self):
+        # generate an "x" (1 < x < (p-1)/2).
+        q = (self.p - 1) // 2
+        qnorm = util.deflate_long(q, 0)
+        qhbyte = byte_ord(qnorm[0])
+        byte_count = len(qnorm)
+        qmask = 0xFF
+        while not (qhbyte & 0x80):
+            qhbyte <<= 1
+            qmask >>= 1
+        while True:
+            x_bytes = os.urandom(byte_count)
+            x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:]
+            x = util.inflate_long(x_bytes, 1)
+            if (x > 1) and (x < q):
+                break
+        self.x = x
+
+    def _parse_kexdh_gex_request(self, m):
+        minbits = m.get_int()
+        preferredbits = m.get_int()
+        maxbits = m.get_int()
+        # smoosh the user's preferred size into our own limits
+        if preferredbits > self.max_bits:
+            preferredbits = self.max_bits
+        if preferredbits < self.min_bits:
+            preferredbits = self.min_bits
+        # fix min/max if they're inconsistent.  technically, we could just pout
+        # and hang up, but there's no harm in giving them the benefit of the
+        # doubt and just picking a bitsize for them.
+        if minbits > preferredbits:
+            minbits = preferredbits
+        if maxbits < preferredbits:
+            maxbits = preferredbits
+        # now save a copy
+        self.min_bits = minbits
+        self.preferred_bits = preferredbits
+        self.max_bits = maxbits
+        # generate prime
+        pack = self.transport._get_modulus_pack()
+        if pack is None:
+            raise SSHException("Can't do server-side gex with no modulus pack")
+        self.transport._log(
+            DEBUG,
+            "Picking p ({} <= {} <= {} bits)".format(
+                minbits, preferredbits, maxbits
+            ),
+        )
+        self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
+        m = Message()
+        m.add_byte(c_MSG_KEXDH_GEX_GROUP)
+        m.add_mpint(self.p)
+        m.add_mpint(self.g)
+        self.transport._send_message(m)
+        self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
+
+    def _parse_kexdh_gex_request_old(self, m):
+        # same as above, but without min_bits or max_bits (used by older
+        # clients like putty)
+        self.preferred_bits = m.get_int()
+        # smoosh the user's preferred size into our own limits
+        if self.preferred_bits > self.max_bits:
+            self.preferred_bits = self.max_bits
+        if self.preferred_bits < self.min_bits:
+            self.preferred_bits = self.min_bits
+        # generate prime
+        pack = self.transport._get_modulus_pack()
+        if pack is None:
+            raise SSHException("Can't do server-side gex with no modulus pack")
+        self.transport._log(
+            DEBUG, "Picking p (~ {} bits)".format(self.preferred_bits)
+        )
+        self.g, self.p = pack.get_modulus(
+            self.min_bits, self.preferred_bits, self.max_bits
+        )
+        m = Message()
+        m.add_byte(c_MSG_KEXDH_GEX_GROUP)
+        m.add_mpint(self.p)
+        m.add_mpint(self.g)
+        self.transport._send_message(m)
+        self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
+        self.old_style = True
+
+    def _parse_kexdh_gex_group(self, m):
+        self.p = m.get_mpint()
+        self.g = m.get_mpint()
+        # reject if p's bit length < 1024 or > 8192
+        bitlen = util.bit_length(self.p)
+        if (bitlen < 1024) or (bitlen > 8192):
+            raise SSHException(
+                "Server-generated gex p (don't ask) is out of range "
+                "({} bits)".format(bitlen)
+            )
+        self.transport._log(DEBUG, "Got server p ({} bits)".format(bitlen))
+        self._generate_x()
+        # now compute e = g^x mod p
+        self.e = pow(self.g, self.x, self.p)
+        m = Message()
+        m.add_byte(c_MSG_KEXDH_GEX_INIT)
+        m.add_mpint(self.e)
+        self.transport._send_message(m)
+        self.transport._expect_packet(_MSG_KEXDH_GEX_REPLY)
+
+    def _parse_kexdh_gex_init(self, m):
+        self.e = m.get_mpint()
+        if (self.e < 1) or (self.e > self.p - 1):
+            raise SSHException('Client kex "e" is out of range')
+        self._generate_x()
+        self.f = pow(self.g, self.x, self.p)
+        K = pow(self.e, self.x, self.p)
+        key = self.transport.get_server_key().asbytes()
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)  # noqa
+        hm = Message()
+        hm.add(
+            self.transport.remote_version,
+            self.transport.local_version,
+            self.transport.remote_kex_init,
+            self.transport.local_kex_init,
+            key,
+        )
+        if not self.old_style:
+            hm.add_int(self.min_bits)
+        hm.add_int(self.preferred_bits)
+        if not self.old_style:
+            hm.add_int(self.max_bits)
+        hm.add_mpint(self.p)
+        hm.add_mpint(self.g)
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        H = self.hash_algo(hm.asbytes()).digest()
+        self.transport._set_K_H(K, H)
+        # sign it
+        sig = self.transport.get_server_key().sign_ssh_data(
+            H, self.transport.host_key_type
+        )
+        # send reply
+        m = Message()
+        m.add_byte(c_MSG_KEXDH_GEX_REPLY)
+        m.add_string(key)
+        m.add_mpint(self.f)
+        m.add_string(sig)
+        self.transport._send_message(m)
+        self.transport._activate_outbound()
+
+    def _parse_kexdh_gex_reply(self, m):
+        host_key = m.get_string()
+        self.f = m.get_mpint()
+        sig = m.get_string()
+        if (self.f < 1) or (self.f > self.p - 1):
+            raise SSHException('Server kex "f" is out of range')
+        K = pow(self.f, self.x, self.p)
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)  # noqa
+        hm = Message()
+        hm.add(
+            self.transport.local_version,
+            self.transport.remote_version,
+            self.transport.local_kex_init,
+            self.transport.remote_kex_init,
+            host_key,
+        )
+        if not self.old_style:
+            hm.add_int(self.min_bits)
+        hm.add_int(self.preferred_bits)
+        if not self.old_style:
+            hm.add_int(self.max_bits)
+        hm.add_mpint(self.p)
+        hm.add_mpint(self.g)
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
+        self.transport._verify_key(host_key, sig)
+        self.transport._activate_outbound()
+
+
+class KexGexSHA256(KexGex):
+    name = "diffie-hellman-group-exchange-sha256"
+    hash_algo = sha256
diff --git a/paramiko/kex_group1.py b/paramiko/kex_group1.py
new file mode 100644
index 0000000..f074256
--- /dev/null
+++ b/paramiko/kex_group1.py
@@ -0,0 +1,155 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Standard SSH key exchange ("kex" if you wanna sound cool).  Diffie-Hellman of
+1024 bit key halves, using a known "p" prime and "g" generator.
+"""
+
+import os
+from hashlib import sha1
+
+from paramiko import util
+from paramiko.common import max_byte, zero_byte, byte_chr, byte_mask
+from paramiko.message import Message
+from paramiko.ssh_exception import SSHException
+
+
+_MSG_KEXDH_INIT, _MSG_KEXDH_REPLY = range(30, 32)
+c_MSG_KEXDH_INIT, c_MSG_KEXDH_REPLY = [byte_chr(c) for c in range(30, 32)]
+
+b7fffffffffffffff = byte_chr(0x7F) + max_byte * 7
+b0000000000000000 = zero_byte * 8
+
+
+class KexGroup1:
+
+    # draft-ietf-secsh-transport-09.txt, page 17
+    P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF  # noqa
+    G = 2
+
+    name = "diffie-hellman-group1-sha1"
+    hash_algo = sha1
+
+    def __init__(self, transport):
+        self.transport = transport
+        self.x = 0
+        self.e = 0
+        self.f = 0
+
+    def start_kex(self):
+        self._generate_x()
+        if self.transport.server_mode:
+            # compute f = g^x mod p, but don't send it yet
+            self.f = pow(self.G, self.x, self.P)
+            self.transport._expect_packet(_MSG_KEXDH_INIT)
+            return
+        # compute e = g^x mod p (where g=2), and send it
+        self.e = pow(self.G, self.x, self.P)
+        m = Message()
+        m.add_byte(c_MSG_KEXDH_INIT)
+        m.add_mpint(self.e)
+        self.transport._send_message(m)
+        self.transport._expect_packet(_MSG_KEXDH_REPLY)
+
+    def parse_next(self, ptype, m):
+        if self.transport.server_mode and (ptype == _MSG_KEXDH_INIT):
+            return self._parse_kexdh_init(m)
+        elif not self.transport.server_mode and (ptype == _MSG_KEXDH_REPLY):
+            return self._parse_kexdh_reply(m)
+        msg = "KexGroup1 asked to handle packet type {:d}"
+        raise SSHException(msg.format(ptype))
+
+    # ...internals...
+
+    def _generate_x(self):
+        # generate an "x" (1 < x < q), where q is (p-1)/2.
+        # p is a 128-byte (1024-bit) number, where the first 64 bits are 1.
+        # therefore q can be approximated as a 2^1023.  we drop the subset of
+        # potential x where the first 63 bits are 1, because some of those
+        # will be larger than q (but this is a tiny tiny subset of
+        # potential x).
+        while 1:
+            x_bytes = os.urandom(128)
+            x_bytes = byte_mask(x_bytes[0], 0x7F) + x_bytes[1:]
+            if (
+                x_bytes[:8] != b7fffffffffffffff
+                and x_bytes[:8] != b0000000000000000
+            ):
+                break
+        self.x = util.inflate_long(x_bytes)
+
+    def _parse_kexdh_reply(self, m):
+        # client mode
+        host_key = m.get_string()
+        self.f = m.get_mpint()
+        if (self.f < 1) or (self.f > self.P - 1):
+            raise SSHException('Server kex "f" is out of range')
+        sig = m.get_binary()
+        K = pow(self.f, self.x, self.P)
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || e || f || K)
+        hm = Message()
+        hm.add(
+            self.transport.local_version,
+            self.transport.remote_version,
+            self.transport.local_kex_init,
+            self.transport.remote_kex_init,
+        )
+        hm.add_string(host_key)
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
+        self.transport._verify_key(host_key, sig)
+        self.transport._activate_outbound()
+
+    def _parse_kexdh_init(self, m):
+        # server mode
+        self.e = m.get_mpint()
+        if (self.e < 1) or (self.e > self.P - 1):
+            raise SSHException('Client kex "e" is out of range')
+        K = pow(self.e, self.x, self.P)
+        key = self.transport.get_server_key().asbytes()
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || e || f || K)
+        hm = Message()
+        hm.add(
+            self.transport.remote_version,
+            self.transport.local_version,
+            self.transport.remote_kex_init,
+            self.transport.local_kex_init,
+        )
+        hm.add_string(key)
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        H = self.hash_algo(hm.asbytes()).digest()
+        self.transport._set_K_H(K, H)
+        # sign it
+        sig = self.transport.get_server_key().sign_ssh_data(
+            H, self.transport.host_key_type
+        )
+        # send reply
+        m = Message()
+        m.add_byte(c_MSG_KEXDH_REPLY)
+        m.add_string(key)
+        m.add_mpint(self.f)
+        m.add_string(sig)
+        self.transport._send_message(m)
+        self.transport._activate_outbound()
diff --git a/paramiko/kex_group14.py b/paramiko/kex_group14.py
new file mode 100644
index 0000000..8dee551
--- /dev/null
+++ b/paramiko/kex_group14.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2013  Torsten Landschoff <torsten@debian.org>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Standard SSH key exchange ("kex" if you wanna sound cool).  Diffie-Hellman of
+2048 bit key halves, using a known "p" prime and "g" generator.
+"""
+
+from paramiko.kex_group1 import KexGroup1
+from hashlib import sha1, sha256
+
+
+class KexGroup14(KexGroup1):
+
+    # http://tools.ietf.org/html/rfc3526#section-3
+    P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF  # noqa
+    G = 2
+
+    name = "diffie-hellman-group14-sha1"
+    hash_algo = sha1
+
+
+class KexGroup14SHA256(KexGroup14):
+    name = "diffie-hellman-group14-sha256"
+    hash_algo = sha256
diff --git a/paramiko/kex_group16.py b/paramiko/kex_group16.py
new file mode 100644
index 0000000..c675f87
--- /dev/null
+++ b/paramiko/kex_group16.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2019 Edgar Sousa <https://github.com/edgsousa>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Standard SSH key exchange ("kex" if you wanna sound cool).  Diffie-Hellman of
+4096 bit key halves, using a known "p" prime and "g" generator.
+"""
+
+from paramiko.kex_group1 import KexGroup1
+from hashlib import sha512
+
+
+class KexGroup16SHA512(KexGroup1):
+    name = "diffie-hellman-group16-sha512"
+    # http://tools.ietf.org/html/rfc3526#section-5
+    P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF  # noqa
+    G = 2
+
+    name = "diffie-hellman-group16-sha512"
+    hash_algo = sha512
diff --git a/paramiko/kex_gss.py b/paramiko/kex_gss.py
new file mode 100644
index 0000000..2a5f29e
--- /dev/null
+++ b/paramiko/kex_gss.py
@@ -0,0 +1,686 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+# Copyright (C) 2013-2014 science + computing ag
+# Author: Sebastian Deiss <sebastian.deiss@t-online.de>
+#
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+
+"""
+This module provides GSS-API / SSPI Key Exchange as defined in :rfc:`4462`.
+
+.. note:: Credential delegation is not supported in server mode.
+
+.. note::
+    `RFC 4462 Section 2.2
+    <https://tools.ietf.org/html/rfc4462.html#section-2.2>`_ says we are not
+    required to implement GSS-API error messages. Thus, in many methods within
+    this module, if an error occurs an exception will be thrown and the
+    connection will be terminated.
+
+.. seealso:: :doc:`/api/ssh_gss`
+
+.. versionadded:: 1.15
+"""
+
+import os
+from hashlib import sha1
+
+from paramiko.common import (
+    DEBUG,
+    max_byte,
+    zero_byte,
+    byte_chr,
+    byte_mask,
+    byte_ord,
+)
+from paramiko import util
+from paramiko.message import Message
+from paramiko.ssh_exception import SSHException
+
+
+(
+    MSG_KEXGSS_INIT,
+    MSG_KEXGSS_CONTINUE,
+    MSG_KEXGSS_COMPLETE,
+    MSG_KEXGSS_HOSTKEY,
+    MSG_KEXGSS_ERROR,
+) = range(30, 35)
+(MSG_KEXGSS_GROUPREQ, MSG_KEXGSS_GROUP) = range(40, 42)
+(
+    c_MSG_KEXGSS_INIT,
+    c_MSG_KEXGSS_CONTINUE,
+    c_MSG_KEXGSS_COMPLETE,
+    c_MSG_KEXGSS_HOSTKEY,
+    c_MSG_KEXGSS_ERROR,
+) = [byte_chr(c) for c in range(30, 35)]
+(c_MSG_KEXGSS_GROUPREQ, c_MSG_KEXGSS_GROUP) = [
+    byte_chr(c) for c in range(40, 42)
+]
+
+
+class KexGSSGroup1:
+    """
+    GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange as defined in `RFC
+    4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
+    """
+
+    # draft-ietf-secsh-transport-09.txt, page 17
+    P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF  # noqa
+    G = 2
+    b7fffffffffffffff = byte_chr(0x7F) + max_byte * 7  # noqa
+    b0000000000000000 = zero_byte * 8  # noqa
+    NAME = "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g=="
+
+    def __init__(self, transport):
+        self.transport = transport
+        self.kexgss = self.transport.kexgss_ctxt
+        self.gss_host = None
+        self.x = 0
+        self.e = 0
+        self.f = 0
+
+    def start_kex(self):
+        """
+        Start the GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange.
+        """
+        self._generate_x()
+        if self.transport.server_mode:
+            # compute f = g^x mod p, but don't send it yet
+            self.f = pow(self.G, self.x, self.P)
+            self.transport._expect_packet(MSG_KEXGSS_INIT)
+            return
+        # compute e = g^x mod p (where g=2), and send it
+        self.e = pow(self.G, self.x, self.P)
+        # Initialize GSS-API Key Exchange
+        self.gss_host = self.transport.gss_host
+        m = Message()
+        m.add_byte(c_MSG_KEXGSS_INIT)
+        m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
+        m.add_mpint(self.e)
+        self.transport._send_message(m)
+        self.transport._expect_packet(
+            MSG_KEXGSS_HOSTKEY,
+            MSG_KEXGSS_CONTINUE,
+            MSG_KEXGSS_COMPLETE,
+            MSG_KEXGSS_ERROR,
+        )
+
+    def parse_next(self, ptype, m):
+        """
+        Parse the next packet.
+
+        :param ptype: The (string) type of the incoming packet
+        :param `.Message` m: The packet content
+        """
+        if self.transport.server_mode and (ptype == MSG_KEXGSS_INIT):
+            return self._parse_kexgss_init(m)
+        elif not self.transport.server_mode and (ptype == MSG_KEXGSS_HOSTKEY):
+            return self._parse_kexgss_hostkey(m)
+        elif self.transport.server_mode and (ptype == MSG_KEXGSS_CONTINUE):
+            return self._parse_kexgss_continue(m)
+        elif not self.transport.server_mode and (ptype == MSG_KEXGSS_COMPLETE):
+            return self._parse_kexgss_complete(m)
+        elif ptype == MSG_KEXGSS_ERROR:
+            return self._parse_kexgss_error(m)
+        msg = "GSS KexGroup1 asked to handle packet type {:d}"
+        raise SSHException(msg.format(ptype))
+
+    # ##  internals...
+
+    def _generate_x(self):
+        """
+        generate an "x" (1 < x < q), where q is (p-1)/2.
+        p is a 128-byte (1024-bit) number, where the first 64 bits are 1.
+        therefore q can be approximated as a 2^1023.  we drop the subset of
+        potential x where the first 63 bits are 1, because some of those will
+        be larger than q (but this is a tiny tiny subset of potential x).
+        """
+        while 1:
+            x_bytes = os.urandom(128)
+            x_bytes = byte_mask(x_bytes[0], 0x7F) + x_bytes[1:]
+            first = x_bytes[:8]
+            if first not in (self.b7fffffffffffffff, self.b0000000000000000):
+                break
+        self.x = util.inflate_long(x_bytes)
+
+    def _parse_kexgss_hostkey(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
+
+        :param `.Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
+        """
+        # client mode
+        host_key = m.get_string()
+        self.transport.host_key = host_key
+        sig = m.get_string()
+        self.transport._verify_key(host_key, sig)
+        self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
+
+    def _parse_kexgss_continue(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_CONTINUE message.
+
+        :param `.Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE
+            message
+        """
+        if not self.transport.server_mode:
+            srv_token = m.get_string()
+            m = Message()
+            m.add_byte(c_MSG_KEXGSS_CONTINUE)
+            m.add_string(
+                self.kexgss.ssh_init_sec_context(
+                    target=self.gss_host, recv_token=srv_token
+                )
+            )
+            self.transport.send_message(m)
+            self.transport._expect_packet(
+                MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
+            )
+        else:
+            pass
+
+    def _parse_kexgss_complete(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
+
+        :param `.Message` m: The content of the
+            SSH2_MSG_KEXGSS_COMPLETE message
+        """
+        # client mode
+        if self.transport.host_key is None:
+            self.transport.host_key = NullHostKey()
+        self.f = m.get_mpint()
+        if (self.f < 1) or (self.f > self.P - 1):
+            raise SSHException('Server kex "f" is out of range')
+        mic_token = m.get_string()
+        # This must be TRUE, if there is a GSS-API token in this message.
+        bool = m.get_boolean()
+        srv_token = None
+        if bool:
+            srv_token = m.get_string()
+        K = pow(self.f, self.x, self.P)
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || e || f || K)
+        hm = Message()
+        hm.add(
+            self.transport.local_version,
+            self.transport.remote_version,
+            self.transport.local_kex_init,
+            self.transport.remote_kex_init,
+        )
+        hm.add_string(self.transport.host_key.__str__())
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        H = sha1(str(hm)).digest()
+        self.transport._set_K_H(K, H)
+        if srv_token is not None:
+            self.kexgss.ssh_init_sec_context(
+                target=self.gss_host, recv_token=srv_token
+            )
+            self.kexgss.ssh_check_mic(mic_token, H)
+        else:
+            self.kexgss.ssh_check_mic(mic_token, H)
+        self.transport.gss_kex_used = True
+        self.transport._activate_outbound()
+
+    def _parse_kexgss_init(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
+
+        :param `.Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
+        """
+        # server mode
+        client_token = m.get_string()
+        self.e = m.get_mpint()
+        if (self.e < 1) or (self.e > self.P - 1):
+            raise SSHException('Client kex "e" is out of range')
+        K = pow(self.e, self.x, self.P)
+        self.transport.host_key = NullHostKey()
+        key = self.transport.host_key.__str__()
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || e || f || K)
+        hm = Message()
+        hm.add(
+            self.transport.remote_version,
+            self.transport.local_version,
+            self.transport.remote_kex_init,
+            self.transport.local_kex_init,
+        )
+        hm.add_string(key)
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        H = sha1(hm.asbytes()).digest()
+        self.transport._set_K_H(K, H)
+        srv_token = self.kexgss.ssh_accept_sec_context(
+            self.gss_host, client_token
+        )
+        m = Message()
+        if self.kexgss._gss_srv_ctxt_status:
+            mic_token = self.kexgss.ssh_get_mic(
+                self.transport.session_id, gss_kex=True
+            )
+            m.add_byte(c_MSG_KEXGSS_COMPLETE)
+            m.add_mpint(self.f)
+            m.add_string(mic_token)
+            if srv_token is not None:
+                m.add_boolean(True)
+                m.add_string(srv_token)
+            else:
+                m.add_boolean(False)
+            self.transport._send_message(m)
+            self.transport.gss_kex_used = True
+            self.transport._activate_outbound()
+        else:
+            m.add_byte(c_MSG_KEXGSS_CONTINUE)
+            m.add_string(srv_token)
+            self.transport._send_message(m)
+            self.transport._expect_packet(
+                MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
+            )
+
+    def _parse_kexgss_error(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).
+        The server may send a GSS-API error message. if it does, we display
+        the error by throwing an exception (client mode).
+
+        :param `.Message` m: The content of the SSH2_MSG_KEXGSS_ERROR message
+        :raise SSHException: Contains GSS-API major and minor status as well as
+                             the error message and the language tag of the
+                             message
+        """
+        maj_status = m.get_int()
+        min_status = m.get_int()
+        err_msg = m.get_string()
+        m.get_string()  # we don't care about the language!
+        raise SSHException(
+            """GSS-API Error:
+Major Status: {}
+Minor Status: {}
+Error Message: {}
+""".format(
+                maj_status, min_status, err_msg
+            )
+        )
+
+
+class KexGSSGroup14(KexGSSGroup1):
+    """
+    GSS-API / SSPI Authenticated Diffie-Hellman Group14 Key Exchange as defined
+    in `RFC 4462 Section 2
+    <https://tools.ietf.org/html/rfc4462.html#section-2>`_
+    """
+
+    P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF  # noqa
+    G = 2
+    NAME = "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g=="
+
+
+class KexGSSGex:
+    """
+    GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange as defined in
+    `RFC 4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
+    """
+
+    NAME = "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g=="
+    min_bits = 1024
+    max_bits = 8192
+    preferred_bits = 2048
+
+    def __init__(self, transport):
+        self.transport = transport
+        self.kexgss = self.transport.kexgss_ctxt
+        self.gss_host = None
+        self.p = None
+        self.q = None
+        self.g = None
+        self.x = None
+        self.e = None
+        self.f = None
+        self.old_style = False
+
+    def start_kex(self):
+        """
+        Start the GSS-API / SSPI Authenticated Diffie-Hellman Group Exchange
+        """
+        if self.transport.server_mode:
+            self.transport._expect_packet(MSG_KEXGSS_GROUPREQ)
+            return
+        # request a bit range: we accept (min_bits) to (max_bits), but prefer
+        # (preferred_bits).  according to the spec, we shouldn't pull the
+        # minimum up above 1024.
+        self.gss_host = self.transport.gss_host
+        m = Message()
+        m.add_byte(c_MSG_KEXGSS_GROUPREQ)
+        m.add_int(self.min_bits)
+        m.add_int(self.preferred_bits)
+        m.add_int(self.max_bits)
+        self.transport._send_message(m)
+        self.transport._expect_packet(MSG_KEXGSS_GROUP)
+
+    def parse_next(self, ptype, m):
+        """
+        Parse the next packet.
+
+        :param ptype: The (string) type of the incoming packet
+        :param `.Message` m: The packet content
+        """
+        if ptype == MSG_KEXGSS_GROUPREQ:
+            return self._parse_kexgss_groupreq(m)
+        elif ptype == MSG_KEXGSS_GROUP:
+            return self._parse_kexgss_group(m)
+        elif ptype == MSG_KEXGSS_INIT:
+            return self._parse_kexgss_gex_init(m)
+        elif ptype == MSG_KEXGSS_HOSTKEY:
+            return self._parse_kexgss_hostkey(m)
+        elif ptype == MSG_KEXGSS_CONTINUE:
+            return self._parse_kexgss_continue(m)
+        elif ptype == MSG_KEXGSS_COMPLETE:
+            return self._parse_kexgss_complete(m)
+        elif ptype == MSG_KEXGSS_ERROR:
+            return self._parse_kexgss_error(m)
+        msg = "KexGex asked to handle packet type {:d}"
+        raise SSHException(msg.format(ptype))
+
+    # ##  internals...
+
+    def _generate_x(self):
+        # generate an "x" (1 < x < (p-1)/2).
+        q = (self.p - 1) // 2
+        qnorm = util.deflate_long(q, 0)
+        qhbyte = byte_ord(qnorm[0])
+        byte_count = len(qnorm)
+        qmask = 0xFF
+        while not (qhbyte & 0x80):
+            qhbyte <<= 1
+            qmask >>= 1
+        while True:
+            x_bytes = os.urandom(byte_count)
+            x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:]
+            x = util.inflate_long(x_bytes, 1)
+            if (x > 1) and (x < q):
+                break
+        self.x = x
+
+    def _parse_kexgss_groupreq(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode).
+
+        :param `.Message` m: The content of the
+            SSH2_MSG_KEXGSS_GROUPREQ message
+        """
+        minbits = m.get_int()
+        preferredbits = m.get_int()
+        maxbits = m.get_int()
+        # smoosh the user's preferred size into our own limits
+        if preferredbits > self.max_bits:
+            preferredbits = self.max_bits
+        if preferredbits < self.min_bits:
+            preferredbits = self.min_bits
+        # fix min/max if they're inconsistent.  technically, we could just pout
+        # and hang up, but there's no harm in giving them the benefit of the
+        # doubt and just picking a bitsize for them.
+        if minbits > preferredbits:
+            minbits = preferredbits
+        if maxbits < preferredbits:
+            maxbits = preferredbits
+        # now save a copy
+        self.min_bits = minbits
+        self.preferred_bits = preferredbits
+        self.max_bits = maxbits
+        # generate prime
+        pack = self.transport._get_modulus_pack()
+        if pack is None:
+            raise SSHException("Can't do server-side gex with no modulus pack")
+        self.transport._log(
+            DEBUG,  # noqa
+            "Picking p ({} <= {} <= {} bits)".format(
+                minbits, preferredbits, maxbits
+            ),
+        )
+        self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
+        m = Message()
+        m.add_byte(c_MSG_KEXGSS_GROUP)
+        m.add_mpint(self.p)
+        m.add_mpint(self.g)
+        self.transport._send_message(m)
+        self.transport._expect_packet(MSG_KEXGSS_INIT)
+
+    def _parse_kexgss_group(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_GROUP message (client mode).
+
+        :param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message
+        """
+        self.p = m.get_mpint()
+        self.g = m.get_mpint()
+        # reject if p's bit length < 1024 or > 8192
+        bitlen = util.bit_length(self.p)
+        if (bitlen < 1024) or (bitlen > 8192):
+            raise SSHException(
+                "Server-generated gex p (don't ask) is out of range "
+                "({} bits)".format(bitlen)
+            )
+        self.transport._log(
+            DEBUG, "Got server p ({} bits)".format(bitlen)
+        )  # noqa
+        self._generate_x()
+        # now compute e = g^x mod p
+        self.e = pow(self.g, self.x, self.p)
+        m = Message()
+        m.add_byte(c_MSG_KEXGSS_INIT)
+        m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
+        m.add_mpint(self.e)
+        self.transport._send_message(m)
+        self.transport._expect_packet(
+            MSG_KEXGSS_HOSTKEY,
+            MSG_KEXGSS_CONTINUE,
+            MSG_KEXGSS_COMPLETE,
+            MSG_KEXGSS_ERROR,
+        )
+
+    def _parse_kexgss_gex_init(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_INIT message (server mode).
+
+        :param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message
+        """
+        client_token = m.get_string()
+        self.e = m.get_mpint()
+        if (self.e < 1) or (self.e > self.p - 1):
+            raise SSHException('Client kex "e" is out of range')
+        self._generate_x()
+        self.f = pow(self.g, self.x, self.p)
+        K = pow(self.e, self.x, self.p)
+        self.transport.host_key = NullHostKey()
+        key = self.transport.host_key.__str__()
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)  # noqa
+        hm = Message()
+        hm.add(
+            self.transport.remote_version,
+            self.transport.local_version,
+            self.transport.remote_kex_init,
+            self.transport.local_kex_init,
+            key,
+        )
+        hm.add_int(self.min_bits)
+        hm.add_int(self.preferred_bits)
+        hm.add_int(self.max_bits)
+        hm.add_mpint(self.p)
+        hm.add_mpint(self.g)
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        H = sha1(hm.asbytes()).digest()
+        self.transport._set_K_H(K, H)
+        srv_token = self.kexgss.ssh_accept_sec_context(
+            self.gss_host, client_token
+        )
+        m = Message()
+        if self.kexgss._gss_srv_ctxt_status:
+            mic_token = self.kexgss.ssh_get_mic(
+                self.transport.session_id, gss_kex=True
+            )
+            m.add_byte(c_MSG_KEXGSS_COMPLETE)
+            m.add_mpint(self.f)
+            m.add_string(mic_token)
+            if srv_token is not None:
+                m.add_boolean(True)
+                m.add_string(srv_token)
+            else:
+                m.add_boolean(False)
+            self.transport._send_message(m)
+            self.transport.gss_kex_used = True
+            self.transport._activate_outbound()
+        else:
+            m.add_byte(c_MSG_KEXGSS_CONTINUE)
+            m.add_string(srv_token)
+            self.transport._send_message(m)
+            self.transport._expect_packet(
+                MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
+            )
+
+    def _parse_kexgss_hostkey(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_HOSTKEY message (client mode).
+
+        :param `Message` m: The content of the SSH2_MSG_KEXGSS_HOSTKEY message
+        """
+        # client mode
+        host_key = m.get_string()
+        self.transport.host_key = host_key
+        sig = m.get_string()
+        self.transport._verify_key(host_key, sig)
+        self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
+
+    def _parse_kexgss_continue(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_CONTINUE message.
+
+        :param `Message` m: The content of the SSH2_MSG_KEXGSS_CONTINUE message
+        """
+        if not self.transport.server_mode:
+            srv_token = m.get_string()
+            m = Message()
+            m.add_byte(c_MSG_KEXGSS_CONTINUE)
+            m.add_string(
+                self.kexgss.ssh_init_sec_context(
+                    target=self.gss_host, recv_token=srv_token
+                )
+            )
+            self.transport.send_message(m)
+            self.transport._expect_packet(
+                MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
+            )
+        else:
+            pass
+
+    def _parse_kexgss_complete(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode).
+
+        :param `Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message
+        """
+        if self.transport.host_key is None:
+            self.transport.host_key = NullHostKey()
+        self.f = m.get_mpint()
+        mic_token = m.get_string()
+        # This must be TRUE, if there is a GSS-API token in this message.
+        bool = m.get_boolean()
+        srv_token = None
+        if bool:
+            srv_token = m.get_string()
+        if (self.f < 1) or (self.f > self.p - 1):
+            raise SSHException('Server kex "f" is out of range')
+        K = pow(self.f, self.x, self.p)
+        # okay, build up the hash H of
+        # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)  # noqa
+        hm = Message()
+        hm.add(
+            self.transport.local_version,
+            self.transport.remote_version,
+            self.transport.local_kex_init,
+            self.transport.remote_kex_init,
+            self.transport.host_key.__str__(),
+        )
+        if not self.old_style:
+            hm.add_int(self.min_bits)
+        hm.add_int(self.preferred_bits)
+        if not self.old_style:
+            hm.add_int(self.max_bits)
+        hm.add_mpint(self.p)
+        hm.add_mpint(self.g)
+        hm.add_mpint(self.e)
+        hm.add_mpint(self.f)
+        hm.add_mpint(K)
+        H = sha1(hm.asbytes()).digest()
+        self.transport._set_K_H(K, H)
+        if srv_token is not None:
+            self.kexgss.ssh_init_sec_context(
+                target=self.gss_host, recv_token=srv_token
+            )
+            self.kexgss.ssh_check_mic(mic_token, H)
+        else:
+            self.kexgss.ssh_check_mic(mic_token, H)
+        self.transport.gss_kex_used = True
+        self.transport._activate_outbound()
+
+    def _parse_kexgss_error(self, m):
+        """
+        Parse the SSH2_MSG_KEXGSS_ERROR message (client mode).
+        The server may send a GSS-API error message. if it does, we display
+        the error by throwing an exception (client mode).
+
+        :param `Message` m:  The content of the SSH2_MSG_KEXGSS_ERROR message
+        :raise SSHException: Contains GSS-API major and minor status as well as
+                             the error message and the language tag of the
+                             message
+        """
+        maj_status = m.get_int()
+        min_status = m.get_int()
+        err_msg = m.get_string()
+        m.get_string()  # we don't care about the language (lang_tag)!
+        raise SSHException(
+            """GSS-API Error:
+Major Status: {}
+Minor Status: {}
+Error Message: {}
+""".format(
+                maj_status, min_status, err_msg
+            )
+        )
+
+
+class NullHostKey:
+    """
+    This class represents the Null Host Key for GSS-API Key Exchange as defined
+    in `RFC 4462 Section 5
+    <https://tools.ietf.org/html/rfc4462.html#section-5>`_
+    """
+
+    def __init__(self):
+        self.key = ""
+
+    def __str__(self):
+        return self.key
+
+    def get_name(self):
+        return self.key
diff --git a/paramiko/message.py b/paramiko/message.py
new file mode 100644
index 0000000..8c2b3bd
--- /dev/null
+++ b/paramiko/message.py
@@ -0,0 +1,318 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Implementation of an SSH2 "message".
+"""
+
+import struct
+from io import BytesIO
+
+from paramiko import util
+from paramiko.common import zero_byte, max_byte, one_byte
+from paramiko.util import u
+
+
+class Message:
+    """
+    An SSH2 message is a stream of bytes that encodes some combination of
+    strings, integers, bools, and infinite-precision integers.  This class
+    builds or breaks down such a byte stream.
+
+    Normally you don't need to deal with anything this low-level, but it's
+    exposed for people implementing custom extensions, or features that
+    paramiko doesn't support yet.
+    """
+
+    big_int = 0xFF000000
+
+    def __init__(self, content=None):
+        """
+        Create a new SSH2 message.
+
+        :param bytes content:
+            the byte stream to use as the message content (passed in only when
+            decomposing a message).
+        """
+        if content is not None:
+            self.packet = BytesIO(content)
+        else:
+            self.packet = BytesIO()
+
+    def __bytes__(self):
+        return self.asbytes()
+
+    def __repr__(self):
+        """
+        Returns a string representation of this object, for debugging.
+        """
+        return "paramiko.Message(" + repr(self.packet.getvalue()) + ")"
+
+    # TODO 4.0: just merge into __bytes__ (everywhere)
+    def asbytes(self):
+        """
+        Return the byte stream content of this Message, as a `bytes`.
+        """
+        return self.packet.getvalue()
+
+    def rewind(self):
+        """
+        Rewind the message to the beginning as if no items had been parsed
+        out of it yet.
+        """
+        self.packet.seek(0)
+
+    def get_remainder(self):
+        """
+        Return the `bytes` of this message that haven't already been parsed and
+        returned.
+        """
+        position = self.packet.tell()
+        remainder = self.packet.read()
+        self.packet.seek(position)
+        return remainder
+
+    def get_so_far(self):
+        """
+        Returns the `bytes` of this message that have been parsed and
+        returned. The string passed into a message's constructor can be
+        regenerated by concatenating ``get_so_far`` and `get_remainder`.
+        """
+        position = self.packet.tell()
+        self.rewind()
+        return self.packet.read(position)
+
+    def get_bytes(self, n):
+        """
+        Return the next ``n`` bytes of the message, without decomposing into an
+        int, decoded string, etc.  Just the raw bytes are returned. Returns a
+        string of ``n`` zero bytes if there weren't ``n`` bytes remaining in
+        the message.
+        """
+        b = self.packet.read(n)
+        max_pad_size = 1 << 20  # Limit padding to 1 MB
+        if len(b) < n < max_pad_size:
+            return b + zero_byte * (n - len(b))
+        return b
+
+    def get_byte(self):
+        """
+        Return the next byte of the message, without decomposing it.  This
+        is equivalent to `get_bytes(1) <get_bytes>`.
+
+        :return:
+            the next (`bytes`) byte of the message, or ``b'\000'`` if there
+            aren't any bytes remaining.
+        """
+        return self.get_bytes(1)
+
+    def get_boolean(self):
+        """
+        Fetch a boolean from the stream.
+        """
+        b = self.get_bytes(1)
+        return b != zero_byte
+
+    def get_adaptive_int(self):
+        """
+        Fetch an int from the stream.
+
+        :return: a 32-bit unsigned `int`.
+        """
+        byte = self.get_bytes(1)
+        if byte == max_byte:
+            return util.inflate_long(self.get_binary())
+        byte += self.get_bytes(3)
+        return struct.unpack(">I", byte)[0]
+
+    def get_int(self):
+        """
+        Fetch an int from the stream.
+        """
+        return struct.unpack(">I", self.get_bytes(4))[0]
+
+    def get_int64(self):
+        """
+        Fetch a 64-bit int from the stream.
+
+        :return: a 64-bit unsigned integer (`int`).
+        """
+        return struct.unpack(">Q", self.get_bytes(8))[0]
+
+    def get_mpint(self):
+        """
+        Fetch a long int (mpint) from the stream.
+
+        :return: an arbitrary-length integer (`int`).
+        """
+        return util.inflate_long(self.get_binary())
+
+    # TODO 4.0: depending on where this is used internally or downstream, force
+    # users to specify get_binary instead and delete this.
+    def get_string(self):
+        """
+        Fetch a "string" from the stream.  This will actually be a `bytes`
+        object, and may contain unprintable characters.  (It's not unheard of
+        for a string to contain another byte-stream message.)
+        """
+        return self.get_bytes(self.get_int())
+
+    # TODO 4.0: also consider having this take over the get_string name, and
+    # remove this name instead.
+    def get_text(self):
+        """
+        Fetch a Unicode string from the stream.
+
+        This currently operates by attempting to encode the next "string" as
+        ``utf-8``.
+        """
+        return u(self.get_string())
+
+    def get_binary(self):
+        """
+        Alias for `get_string` (obtains a bytestring).
+        """
+        return self.get_bytes(self.get_int())
+
+    def get_list(self):
+        """
+        Fetch a list of `strings <str>` from the stream.
+
+        These are trivially encoded as comma-separated values in a string.
+        """
+        return self.get_text().split(",")
+
+    def add_bytes(self, b):
+        """
+        Write bytes to the stream, without any formatting.
+
+        :param bytes b: bytes to add
+        """
+        self.packet.write(b)
+        return self
+
+    def add_byte(self, b):
+        """
+        Write a single byte to the stream, without any formatting.
+
+        :param bytes b: byte to add
+        """
+        self.packet.write(b)
+        return self
+
+    def add_boolean(self, b):
+        """
+        Add a boolean value to the stream.
+
+        :param bool b: boolean value to add
+        """
+        if b:
+            self.packet.write(one_byte)
+        else:
+            self.packet.write(zero_byte)
+        return self
+
+    def add_int(self, n):
+        """
+        Add an integer to the stream.
+
+        :param int n: integer to add
+        """
+        self.packet.write(struct.pack(">I", n))
+        return self
+
+    def add_adaptive_int(self, n):
+        """
+        Add an integer to the stream.
+
+        :param int n: integer to add
+        """
+        if n >= Message.big_int:
+            self.packet.write(max_byte)
+            self.add_string(util.deflate_long(n))
+        else:
+            self.packet.write(struct.pack(">I", n))
+        return self
+
+    def add_int64(self, n):
+        """
+        Add a 64-bit int to the stream.
+
+        :param int n: long int to add
+        """
+        self.packet.write(struct.pack(">Q", n))
+        return self
+
+    def add_mpint(self, z):
+        """
+        Add a long int to the stream, encoded as an infinite-precision
+        integer.  This method only works on positive numbers.
+
+        :param int z: long int to add
+        """
+        self.add_string(util.deflate_long(z))
+        return self
+
+    # TODO: see the TODO for get_string/get_text/et al, this should change
+    # to match.
+    def add_string(self, s):
+        """
+        Add a bytestring to the stream.
+
+        :param byte s: bytestring to add
+        """
+        s = util.asbytes(s)
+        self.add_int(len(s))
+        self.packet.write(s)
+        return self
+
+    def add_list(self, l):  # noqa: E741
+        """
+        Add a list of strings to the stream.  They are encoded identically to
+        a single string of values separated by commas.  (Yes, really, that's
+        how SSH2 does it.)
+
+        :param l: list of strings to add
+        """
+        self.add_string(",".join(l))
+        return self
+
+    def _add(self, i):
+        if type(i) is bool:
+            return self.add_boolean(i)
+        elif isinstance(i, int):
+            return self.add_adaptive_int(i)
+        elif type(i) is list:
+            return self.add_list(i)
+        else:
+            return self.add_string(i)
+
+    # TODO: this would never have worked for unicode strings under Python 3,
+    # guessing nobody/nothing ever used it for that purpose?
+    def add(self, *seq):
+        """
+        Add a sequence of items to the stream.  The values are encoded based
+        on their type: bytes, str, int, bool, or list.
+
+        .. warning::
+            Longs are encoded non-deterministically.  Don't use this method.
+
+        :param seq: the sequence of items
+        """
+        for item in seq:
+            self._add(item)
diff --git a/paramiko/packet.py b/paramiko/packet.py
new file mode 100644
index 0000000..e40355e
--- /dev/null
+++ b/paramiko/packet.py
@@ -0,0 +1,634 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Packet handling
+"""
+
+import errno
+import os
+import socket
+import struct
+import threading
+import time
+from hmac import HMAC
+
+from paramiko import util
+from paramiko.common import (
+    linefeed_byte,
+    cr_byte_value,
+    MSG_NAMES,
+    DEBUG,
+    xffffffff,
+    zero_byte,
+    byte_ord,
+)
+from paramiko.util import u
+from paramiko.ssh_exception import SSHException, ProxyCommandFailure
+from paramiko.message import Message
+
+
+def compute_hmac(key, message, digest_class):
+    return HMAC(key, message, digest_class).digest()
+
+
+class NeedRekeyException(Exception):
+    """
+    Exception indicating a rekey is needed.
+    """
+
+    pass
+
+
+def first_arg(e):
+    arg = None
+    if type(e.args) is tuple and len(e.args) > 0:
+        arg = e.args[0]
+    return arg
+
+
+class Packetizer:
+    """
+    Implementation of the base SSH packet protocol.
+    """
+
+    # READ the secsh RFC's before raising these values.  if anything,
+    # they should probably be lower.
+    REKEY_PACKETS = pow(2, 29)
+    REKEY_BYTES = pow(2, 29)
+
+    # Allow receiving this many packets after a re-key request before
+    # terminating
+    REKEY_PACKETS_OVERFLOW_MAX = pow(2, 29)
+    # Allow receiving this many bytes after a re-key request before terminating
+    REKEY_BYTES_OVERFLOW_MAX = pow(2, 29)
+
+    def __init__(self, socket):
+        self.__socket = socket
+        self.__logger = None
+        self.__closed = False
+        self.__dump_packets = False
+        self.__need_rekey = False
+        self.__init_count = 0
+        self.__remainder = bytes()
+
+        # used for noticing when to re-key:
+        self.__sent_bytes = 0
+        self.__sent_packets = 0
+        self.__received_bytes = 0
+        self.__received_packets = 0
+        self.__received_bytes_overflow = 0
+        self.__received_packets_overflow = 0
+
+        # current inbound/outbound ciphering:
+        self.__block_size_out = 8
+        self.__block_size_in = 8
+        self.__mac_size_out = 0
+        self.__mac_size_in = 0
+        self.__block_engine_out = None
+        self.__block_engine_in = None
+        self.__sdctr_out = False
+        self.__mac_engine_out = None
+        self.__mac_engine_in = None
+        self.__mac_key_out = bytes()
+        self.__mac_key_in = bytes()
+        self.__compress_engine_out = None
+        self.__compress_engine_in = None
+        self.__sequence_number_out = 0
+        self.__sequence_number_in = 0
+        self.__etm_out = False
+        self.__etm_in = False
+
+        # lock around outbound writes (packet computation)
+        self.__write_lock = threading.RLock()
+
+        # keepalives:
+        self.__keepalive_interval = 0
+        self.__keepalive_last = time.time()
+        self.__keepalive_callback = None
+
+        self.__timer = None
+        self.__handshake_complete = False
+        self.__timer_expired = False
+
+    @property
+    def closed(self):
+        return self.__closed
+
+    def set_log(self, log):
+        """
+        Set the Python log object to use for logging.
+        """
+        self.__logger = log
+
+    def set_outbound_cipher(
+        self,
+        block_engine,
+        block_size,
+        mac_engine,
+        mac_size,
+        mac_key,
+        sdctr=False,
+        etm=False,
+    ):
+        """
+        Switch outbound data cipher.
+        :param etm: Set encrypt-then-mac from OpenSSH
+        """
+        self.__block_engine_out = block_engine
+        self.__sdctr_out = sdctr
+        self.__block_size_out = block_size
+        self.__mac_engine_out = mac_engine
+        self.__mac_size_out = mac_size
+        self.__mac_key_out = mac_key
+        self.__sent_bytes = 0
+        self.__sent_packets = 0
+        self.__etm_out = etm
+        # wait until the reset happens in both directions before clearing
+        # rekey flag
+        self.__init_count |= 1
+        if self.__init_count == 3:
+            self.__init_count = 0
+            self.__need_rekey = False
+
+    def set_inbound_cipher(
+        self,
+        block_engine,
+        block_size,
+        mac_engine,
+        mac_size,
+        mac_key,
+        etm=False,
+    ):
+        """
+        Switch inbound data cipher.
+        :param etm: Set encrypt-then-mac from OpenSSH
+        """
+        self.__block_engine_in = block_engine
+        self.__block_size_in = block_size
+        self.__mac_engine_in = mac_engine
+        self.__mac_size_in = mac_size
+        self.__mac_key_in = mac_key
+        self.__received_bytes = 0
+        self.__received_packets = 0
+        self.__received_bytes_overflow = 0
+        self.__received_packets_overflow = 0
+        self.__etm_in = etm
+        # wait until the reset happens in both directions before clearing
+        # rekey flag
+        self.__init_count |= 2
+        if self.__init_count == 3:
+            self.__init_count = 0
+            self.__need_rekey = False
+
+    def set_outbound_compressor(self, compressor):
+        self.__compress_engine_out = compressor
+
+    def set_inbound_compressor(self, compressor):
+        self.__compress_engine_in = compressor
+
+    def close(self):
+        self.__closed = True
+        self.__socket.close()
+
+    def set_hexdump(self, hexdump):
+        self.__dump_packets = hexdump
+
+    def get_hexdump(self):
+        return self.__dump_packets
+
+    def get_mac_size_in(self):
+        return self.__mac_size_in
+
+    def get_mac_size_out(self):
+        return self.__mac_size_out
+
+    def need_rekey(self):
+        """
+        Returns ``True`` if a new set of keys needs to be negotiated.  This
+        will be triggered during a packet read or write, so it should be
+        checked after every read or write, or at least after every few.
+        """
+        return self.__need_rekey
+
+    def set_keepalive(self, interval, callback):
+        """
+        Turn on/off the callback keepalive.  If ``interval`` seconds pass with
+        no data read from or written to the socket, the callback will be
+        executed and the timer will be reset.
+        """
+        self.__keepalive_interval = interval
+        self.__keepalive_callback = callback
+        self.__keepalive_last = time.time()
+
+    def read_timer(self):
+        self.__timer_expired = True
+
+    def start_handshake(self, timeout):
+        """
+        Tells `Packetizer` that the handshake process started.
+        Starts a book keeping timer that can signal a timeout in the
+        handshake process.
+
+        :param float timeout: amount of seconds to wait before timing out
+        """
+        if not self.__timer:
+            self.__timer = threading.Timer(float(timeout), self.read_timer)
+            self.__timer.start()
+
+    def handshake_timed_out(self):
+        """
+        Checks if the handshake has timed out.
+
+        If `start_handshake` wasn't called before the call to this function,
+        the return value will always be `False`. If the handshake completed
+        before a timeout was reached, the return value will be `False`
+
+        :return: handshake time out status, as a `bool`
+        """
+        if not self.__timer:
+            return False
+        if self.__handshake_complete:
+            return False
+        return self.__timer_expired
+
+    def complete_handshake(self):
+        """
+        Tells `Packetizer` that the handshake has completed.
+        """
+        if self.__timer:
+            self.__timer.cancel()
+            self.__timer_expired = False
+            self.__handshake_complete = True
+
+    def read_all(self, n, check_rekey=False):
+        """
+        Read as close to N bytes as possible, blocking as long as necessary.
+
+        :param int n: number of bytes to read
+        :return: the data read, as a `str`
+
+        :raises:
+            ``EOFError`` -- if the socket was closed before all the bytes could
+            be read
+        """
+        out = bytes()
+        # handle over-reading from reading the banner line
+        if len(self.__remainder) > 0:
+            out = self.__remainder[:n]
+            self.__remainder = self.__remainder[n:]
+            n -= len(out)
+        while n > 0:
+            got_timeout = False
+            if self.handshake_timed_out():
+                raise EOFError()
+            try:
+                x = self.__socket.recv(n)
+                if len(x) == 0:
+                    raise EOFError()
+                out += x
+                n -= len(x)
+            except socket.timeout:
+                got_timeout = True
+            except socket.error as e:
+                # on Linux, sometimes instead of socket.timeout, we get
+                # EAGAIN.  this is a bug in recent (> 2.6.9) kernels but
+                # we need to work around it.
+                arg = first_arg(e)
+                if arg == errno.EAGAIN:
+                    got_timeout = True
+                elif self.__closed:
+                    raise EOFError()
+                else:
+                    raise
+            if got_timeout:
+                if self.__closed:
+                    raise EOFError()
+                if check_rekey and (len(out) == 0) and self.__need_rekey:
+                    raise NeedRekeyException()
+                self._check_keepalive()
+        return out
+
+    def write_all(self, out):
+        self.__keepalive_last = time.time()
+        iteration_with_zero_as_return_value = 0
+        while len(out) > 0:
+            retry_write = False
+            try:
+                n = self.__socket.send(out)
+            except socket.timeout:
+                retry_write = True
+            except socket.error as e:
+                arg = first_arg(e)
+                if arg == errno.EAGAIN:
+                    retry_write = True
+                else:
+                    n = -1
+            except ProxyCommandFailure:
+                raise  # so it doesn't get swallowed by the below catchall
+            except Exception:
+                # could be: (32, 'Broken pipe')
+                n = -1
+            if retry_write:
+                n = 0
+                if self.__closed:
+                    n = -1
+            else:
+                if n == 0 and iteration_with_zero_as_return_value > 10:
+                    # We shouldn't retry the write, but we didn't
+                    # manage to send anything over the socket. This might be an
+                    # indication that we have lost contact with the remote
+                    # side, but are yet to receive an EOFError or other socket
+                    # errors. Let's give it some iteration to try and catch up.
+                    n = -1
+                iteration_with_zero_as_return_value += 1
+            if n < 0:
+                raise EOFError()
+            if n == len(out):
+                break
+            out = out[n:]
+        return
+
+    def readline(self, timeout):
+        """
+        Read a line from the socket.  We assume no data is pending after the
+        line, so it's okay to attempt large reads.
+        """
+        buf = self.__remainder
+        while linefeed_byte not in buf:
+            buf += self._read_timeout(timeout)
+        n = buf.index(linefeed_byte)
+        self.__remainder = buf[n + 1 :]
+        buf = buf[:n]
+        if (len(buf) > 0) and (buf[-1] == cr_byte_value):
+            buf = buf[:-1]
+        return u(buf)
+
+    def send_message(self, data):
+        """
+        Write a block of data using the current cipher, as an SSH block.
+        """
+        # encrypt this sucka
+        data = data.asbytes()
+        cmd = byte_ord(data[0])
+        if cmd in MSG_NAMES:
+            cmd_name = MSG_NAMES[cmd]
+        else:
+            cmd_name = "${:x}".format(cmd)
+        orig_len = len(data)
+        self.__write_lock.acquire()
+        try:
+            if self.__compress_engine_out is not None:
+                data = self.__compress_engine_out(data)
+            packet = self._build_packet(data)
+            if self.__dump_packets:
+                self._log(
+                    DEBUG,
+                    "Write packet <{}>, length {}".format(cmd_name, orig_len),
+                )
+                self._log(DEBUG, util.format_binary(packet, "OUT: "))
+            if self.__block_engine_out is not None:
+                if self.__etm_out:
+                    # packet length is not encrypted in EtM
+                    out = packet[0:4] + self.__block_engine_out.update(
+                        packet[4:]
+                    )
+                else:
+                    out = self.__block_engine_out.update(packet)
+            else:
+                out = packet
+            # + mac
+            if self.__block_engine_out is not None:
+                packed = struct.pack(">I", self.__sequence_number_out)
+                payload = packed + (out if self.__etm_out else packet)
+                out += compute_hmac(
+                    self.__mac_key_out, payload, self.__mac_engine_out
+                )[: self.__mac_size_out]
+            self.__sequence_number_out = (
+                self.__sequence_number_out + 1
+            ) & xffffffff
+            self.write_all(out)
+
+            self.__sent_bytes += len(out)
+            self.__sent_packets += 1
+            sent_too_much = (
+                self.__sent_packets >= self.REKEY_PACKETS
+                or self.__sent_bytes >= self.REKEY_BYTES
+            )
+            if sent_too_much and not self.__need_rekey:
+                # only ask once for rekeying
+                msg = "Rekeying (hit {} packets, {} bytes sent)"
+                self._log(
+                    DEBUG, msg.format(self.__sent_packets, self.__sent_bytes)
+                )
+                self.__received_bytes_overflow = 0
+                self.__received_packets_overflow = 0
+                self._trigger_rekey()
+        finally:
+            self.__write_lock.release()
+
+    def read_message(self):
+        """
+        Only one thread should ever be in this function (no other locking is
+        done).
+
+        :raises: `.SSHException` -- if the packet is mangled
+        :raises: `.NeedRekeyException` -- if the transport should rekey
+        """
+        header = self.read_all(self.__block_size_in, check_rekey=True)
+        if self.__etm_in:
+            packet_size = struct.unpack(">I", header[:4])[0]
+            remaining = packet_size - self.__block_size_in + 4
+            packet = header[4:] + self.read_all(remaining, check_rekey=False)
+            mac = self.read_all(self.__mac_size_in, check_rekey=False)
+            mac_payload = (
+                struct.pack(">II", self.__sequence_number_in, packet_size)
+                + packet
+            )
+            my_mac = compute_hmac(
+                self.__mac_key_in, mac_payload, self.__mac_engine_in
+            )[: self.__mac_size_in]
+            if not util.constant_time_bytes_eq(my_mac, mac):
+                raise SSHException("Mismatched MAC")
+            header = packet
+
+        if self.__block_engine_in is not None:
+            header = self.__block_engine_in.update(header)
+        if self.__dump_packets:
+            self._log(DEBUG, util.format_binary(header, "IN: "))
+
+        # When ETM is in play, we've already read the packet size & decrypted
+        # everything, so just set the packet back to the header we obtained.
+        if self.__etm_in:
+            packet = header
+        # Otherwise, use the older non-ETM logic
+        else:
+            packet_size = struct.unpack(">I", header[:4])[0]
+
+            # leftover contains decrypted bytes from the first block (after the
+            # length field)
+            leftover = header[4:]
+            if (packet_size - len(leftover)) % self.__block_size_in != 0:
+                raise SSHException("Invalid packet blocking")
+            buf = self.read_all(
+                packet_size + self.__mac_size_in - len(leftover)
+            )
+            packet = buf[: packet_size - len(leftover)]
+            post_packet = buf[packet_size - len(leftover) :]
+
+            if self.__block_engine_in is not None:
+                packet = self.__block_engine_in.update(packet)
+            packet = leftover + packet
+
+        if self.__dump_packets:
+            self._log(DEBUG, util.format_binary(packet, "IN: "))
+
+        if self.__mac_size_in > 0 and not self.__etm_in:
+            mac = post_packet[: self.__mac_size_in]
+            mac_payload = (
+                struct.pack(">II", self.__sequence_number_in, packet_size)
+                + packet
+            )
+            my_mac = compute_hmac(
+                self.__mac_key_in, mac_payload, self.__mac_engine_in
+            )[: self.__mac_size_in]
+            if not util.constant_time_bytes_eq(my_mac, mac):
+                raise SSHException("Mismatched MAC")
+        padding = byte_ord(packet[0])
+        payload = packet[1 : packet_size - padding]
+
+        if self.__dump_packets:
+            self._log(
+                DEBUG,
+                "Got payload ({} bytes, {} padding)".format(
+                    packet_size, padding
+                ),
+            )
+
+        if self.__compress_engine_in is not None:
+            payload = self.__compress_engine_in(payload)
+
+        msg = Message(payload[1:])
+        msg.seqno = self.__sequence_number_in
+        self.__sequence_number_in = (self.__sequence_number_in + 1) & xffffffff
+
+        # check for rekey
+        raw_packet_size = packet_size + self.__mac_size_in + 4
+        self.__received_bytes += raw_packet_size
+        self.__received_packets += 1
+        if self.__need_rekey:
+            # we've asked to rekey -- give them some packets to comply before
+            # dropping the connection
+            self.__received_bytes_overflow += raw_packet_size
+            self.__received_packets_overflow += 1
+            if (
+                self.__received_packets_overflow
+                >= self.REKEY_PACKETS_OVERFLOW_MAX
+            ) or (
+                self.__received_bytes_overflow >= self.REKEY_BYTES_OVERFLOW_MAX
+            ):
+                raise SSHException(
+                    "Remote transport is ignoring rekey requests"
+                )
+        elif (self.__received_packets >= self.REKEY_PACKETS) or (
+            self.__received_bytes >= self.REKEY_BYTES
+        ):
+            # only ask once for rekeying
+            err = "Rekeying (hit {} packets, {} bytes received)"
+            self._log(
+                DEBUG,
+                err.format(self.__received_packets, self.__received_bytes),
+            )
+            self.__received_bytes_overflow = 0
+            self.__received_packets_overflow = 0
+            self._trigger_rekey()
+
+        cmd = byte_ord(payload[0])
+        if cmd in MSG_NAMES:
+            cmd_name = MSG_NAMES[cmd]
+        else:
+            cmd_name = "${:x}".format(cmd)
+        if self.__dump_packets:
+            self._log(
+                DEBUG,
+                "Read packet <{}>, length {}".format(cmd_name, len(payload)),
+            )
+        return cmd, msg
+
+    # ...protected...
+
+    def _log(self, level, msg):
+        if self.__logger is None:
+            return
+        if issubclass(type(msg), list):
+            for m in msg:
+                self.__logger.log(level, m)
+        else:
+            self.__logger.log(level, msg)
+
+    def _check_keepalive(self):
+        if (
+            not self.__keepalive_interval
+            or not self.__block_engine_out
+            or self.__need_rekey
+        ):
+            # wait till we're encrypting, and not in the middle of rekeying
+            return
+        now = time.time()
+        if now > self.__keepalive_last + self.__keepalive_interval:
+            self.__keepalive_callback()
+            self.__keepalive_last = now
+
+    def _read_timeout(self, timeout):
+        start = time.time()
+        while True:
+            try:
+                x = self.__socket.recv(128)
+                if len(x) == 0:
+                    raise EOFError()
+                break
+            except socket.timeout:
+                pass
+            if self.__closed:
+                raise EOFError()
+            now = time.time()
+            if now - start >= timeout:
+                raise socket.timeout()
+        return x
+
+    def _build_packet(self, payload):
+        # pad up at least 4 bytes, to nearest block-size (usually 8)
+        bsize = self.__block_size_out
+        # do not include payload length in computations for padding in EtM mode
+        # (payload length won't be encrypted)
+        addlen = 4 if self.__etm_out else 8
+        padding = 3 + bsize - ((len(payload) + addlen) % bsize)
+        packet = struct.pack(">IB", len(payload) + padding + 1, padding)
+        packet += payload
+        if self.__sdctr_out or self.__block_engine_out is None:
+            # cute trick i caught openssh doing: if we're not encrypting or
+            # SDCTR mode (RFC4344),
+            # don't waste random bytes for the padding
+            packet += zero_byte * padding
+        else:
+            packet += os.urandom(padding)
+        return packet
+
+    def _trigger_rekey(self):
+        # outside code should check for this flag
+        self.__need_rekey = True
diff --git a/paramiko/pipe.py b/paramiko/pipe.py
new file mode 100644
index 0000000..65944fa
--- /dev/null
+++ b/paramiko/pipe.py
@@ -0,0 +1,148 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Abstraction of a one-way pipe where the read end can be used in
+`select.select`. Normally this is trivial, but Windows makes it nearly
+impossible.
+
+The pipe acts like an Event, which can be set or cleared. When set, the pipe
+will trigger as readable in `select <select.select>`.
+"""
+
+import sys
+import os
+import socket
+
+
+def make_pipe():
+    if sys.platform[:3] != "win":
+        p = PosixPipe()
+    else:
+        p = WindowsPipe()
+    return p
+
+
+class PosixPipe:
+    def __init__(self):
+        self._rfd, self._wfd = os.pipe()
+        self._set = False
+        self._forever = False
+        self._closed = False
+
+    def close(self):
+        os.close(self._rfd)
+        os.close(self._wfd)
+        # used for unit tests:
+        self._closed = True
+
+    def fileno(self):
+        return self._rfd
+
+    def clear(self):
+        if not self._set or self._forever:
+            return
+        os.read(self._rfd, 1)
+        self._set = False
+
+    def set(self):
+        if self._set or self._closed:
+            return
+        self._set = True
+        os.write(self._wfd, b"*")
+
+    def set_forever(self):
+        self._forever = True
+        self.set()
+
+
+class WindowsPipe:
+    """
+    On Windows, only an OS-level "WinSock" may be used in select(), but reads
+    and writes must be to the actual socket object.
+    """
+
+    def __init__(self):
+        serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        serv.bind(("127.0.0.1", 0))
+        serv.listen(1)
+
+        # need to save sockets in _rsock/_wsock so they don't get closed
+        self._rsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._rsock.connect(("127.0.0.1", serv.getsockname()[1]))
+
+        self._wsock, addr = serv.accept()
+        serv.close()
+        self._set = False
+        self._forever = False
+        self._closed = False
+
+    def close(self):
+        self._rsock.close()
+        self._wsock.close()
+        # used for unit tests:
+        self._closed = True
+
+    def fileno(self):
+        return self._rsock.fileno()
+
+    def clear(self):
+        if not self._set or self._forever:
+            return
+        self._rsock.recv(1)
+        self._set = False
+
+    def set(self):
+        if self._set or self._closed:
+            return
+        self._set = True
+        self._wsock.send(b"*")
+
+    def set_forever(self):
+        self._forever = True
+        self.set()
+
+
+class OrPipe:
+    def __init__(self, pipe):
+        self._set = False
+        self._partner = None
+        self._pipe = pipe
+
+    def set(self):
+        self._set = True
+        if not self._partner._set:
+            self._pipe.set()
+
+    def clear(self):
+        self._set = False
+        if not self._partner._set:
+            self._pipe.clear()
+
+
+def make_or_pipe(pipe):
+    """
+    wraps a pipe into two pipe-like objects which are "or"d together to
+    affect the real pipe. if either returned pipe is set, the wrapped pipe
+    is set. when both are cleared, the wrapped pipe is cleared.
+    """
+    p1 = OrPipe(pipe)
+    p2 = OrPipe(pipe)
+    p1._partner = p2
+    p2._partner = p1
+    return p1, p2
diff --git a/paramiko/pkey.py b/paramiko/pkey.py
new file mode 100644
index 0000000..ff6095a
--- /dev/null
+++ b/paramiko/pkey.py
@@ -0,0 +1,747 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Common API for all public keys.
+"""
+
+import base64
+from base64 import encodebytes, decodebytes
+from binascii import unhexlify
+import os
+from hashlib import md5
+import re
+import struct
+
+import bcrypt
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.ciphers import algorithms, modes, Cipher
+
+from paramiko import util
+from paramiko.util import u, b
+from paramiko.common import o600
+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 = 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 data[i - padding_length] != i + 1:
+            raise SSHException("Invalid key")
+    return data[:-padding_length]
+
+
+class PKey:
+    """
+    Base class for public keys.
+    """
+
+    # known encryption types for private key files:
+    _CIPHER_TABLE = {
+        "AES-128-CBC": {
+            "cipher": algorithms.AES,
+            "keysize": 16,
+            "blocksize": 16,
+            "mode": modes.CBC,
+        },
+        "AES-256-CBC": {
+            "cipher": algorithms.AES,
+            "keysize": 32,
+            "blocksize": 16,
+            "mode": modes.CBC,
+        },
+        "DES-EDE3-CBC": {
+            "cipher": algorithms.TripleDES,
+            "keysize": 24,
+            "blocksize": 8,
+            "mode": modes.CBC,
+        },
+    }
+    _PRIVATE_KEY_FORMAT_ORIGINAL = 1
+    _PRIVATE_KEY_FORMAT_OPENSSH = 2
+    BEGIN_TAG = re.compile(
+        r"^-{5}BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY-{5}\s*$"
+    )
+    END_TAG = re.compile(r"^-{5}END (RSA|DSA|EC|OPENSSH) PRIVATE KEY-{5}\s*$")
+
+    def __init__(self, msg=None, data=None):
+        """
+        Create a new instance of this public key type.  If ``msg`` is given,
+        the key's public part(s) will be filled in from the message.  If
+        ``data`` is given, the key's public part(s) will be filled in from
+        the string.
+
+        :param .Message msg:
+            an optional SSH `.Message` containing a public key of this type.
+        :param str data: an optional string containing a public key
+            of this type
+
+        :raises: `.SSHException` --
+            if a key cannot be created from the ``data`` or ``msg`` given, or
+            no key was passed in.
+        """
+        pass
+
+    # TODO 4.0: just merge into __bytes__ (everywhere)
+    def asbytes(self):
+        """
+        Return a string of an SSH `.Message` made up of the public part(s) of
+        this key.  This string is suitable for passing to `__init__` to
+        re-create the key object later.
+        """
+        return bytes()
+
+    def __bytes__(self):
+        return self.asbytes()
+
+    def __eq__(self, other):
+        return isinstance(other, PKey) and self._fields == other._fields
+
+    def __hash__(self):
+        return hash(self._fields)
+
+    @property
+    def _fields(self):
+        raise NotImplementedError
+
+    def get_name(self):
+        """
+        Return the name of this private key implementation.
+
+        :return:
+            name of this private key type, in SSH terminology, as a `str` (for
+            example, ``"ssh-rsa"``).
+        """
+        return ""
+
+    def get_bits(self):
+        """
+        Return the number of significant bits in this key.  This is useful
+        for judging the relative security of a key.
+
+        :return: bits in the key (as an `int`)
+        """
+        return 0
+
+    def can_sign(self):
+        """
+        Return ``True`` if this key has the private part necessary for signing
+        data.
+        """
+        return False
+
+    def get_fingerprint(self):
+        """
+        Return an MD5 fingerprint of the public part of this key.  Nothing
+        secret is revealed.
+
+        :return:
+            a 16-byte `string <str>` (binary) of the MD5 fingerprint, in SSH
+            format.
+        """
+        return md5(self.asbytes()).digest()
+
+    def get_base64(self):
+        """
+        Return a base64 string containing the public part of this key.  Nothing
+        secret is revealed.  This format is compatible with that used to store
+        public key files or recognized host keys.
+
+        :return: a base64 `string <str>` containing the public part of the key.
+        """
+        return u(encodebytes(self.asbytes())).replace("\n", "")
+
+    def sign_ssh_data(self, data, algorithm=None):
+        """
+        Sign a blob of data with this private key, and return a `.Message`
+        representing an SSH signature message.
+
+        :param bytes data:
+            the data to sign.
+        :param str algorithm:
+            the signature algorithm to use, if different from the key's
+            internal name. Default: ``None``.
+        :return: an SSH signature `message <.Message>`.
+
+        .. versionchanged:: 2.9
+            Added the ``algorithm`` kwarg.
+        """
+        return bytes()
+
+    def verify_ssh_sig(self, data, msg):
+        """
+        Given a blob of data, and an SSH message representing a signature of
+        that data, verify that it was signed with this key.
+
+        :param bytes data: the data that was signed.
+        :param .Message msg: an SSH signature message
+        :return:
+            ``True`` if the signature verifies correctly; ``False`` otherwise.
+        """
+        return False
+
+    @classmethod
+    def from_private_key_file(cls, filename, password=None):
+        """
+        Create a key object by reading a private key file.  If the private
+        key is encrypted and ``password`` is not ``None``, the given password
+        will be used to decrypt the key (otherwise `.PasswordRequiredException`
+        is thrown).  Through the magic of Python, this factory method will
+        exist in all subclasses of PKey (such as `.RSAKey` or `.DSSKey`), but
+        is useless on the abstract PKey class.
+
+        :param str filename: name of the file to read
+        :param str password:
+            an optional password to use to decrypt the key file, if it's
+            encrypted
+        :return: a new `.PKey` based on the given private key
+
+        :raises: ``IOError`` -- if there was an error reading the file
+        :raises: `.PasswordRequiredException` -- if the private key file is
+            encrypted, and ``password`` is ``None``
+        :raises: `.SSHException` -- if the key file is invalid
+        """
+        key = cls(filename=filename, password=password)
+        return key
+
+    @classmethod
+    def from_private_key(cls, file_obj, password=None):
+        """
+        Create a key object by reading a private key from a file (or file-like)
+        object.  If the private key is encrypted and ``password`` is not
+        ``None``, the given password will be used to decrypt the key (otherwise
+        `.PasswordRequiredException` is thrown).
+
+        :param file_obj: the file-like object to read from
+        :param str password:
+            an optional password to use to decrypt the key, if it's encrypted
+        :return: a new `.PKey` based on the given private key
+
+        :raises: ``IOError`` -- if there was an error reading the key
+        :raises: `.PasswordRequiredException` --
+            if the private key file is encrypted, and ``password`` is ``None``
+        :raises: `.SSHException` -- if the key file is invalid
+        """
+        key = cls(file_obj=file_obj, password=password)
+        return key
+
+    def write_private_key_file(self, filename, password=None):
+        """
+        Write private key contents into a file.  If the password is not
+        ``None``, the key is encrypted before writing.
+
+        :param str filename: name of the file to write
+        :param str password:
+            an optional password to use to encrypt the key file
+
+        :raises: ``IOError`` -- if there was an error writing the file
+        :raises: `.SSHException` -- if the key is invalid
+        """
+        raise Exception("Not implemented in PKey")
+
+    def write_private_key(self, file_obj, password=None):
+        """
+        Write private key contents into a file (or file-like) object.  If the
+        password is not ``None``, the key is encrypted before writing.
+
+        :param file_obj: the file-like object to write into
+        :param str password: an optional password to use to encrypt the key
+
+        :raises: ``IOError`` -- if there was an error writing to the file
+        :raises: `.SSHException` -- if the key is invalid
+        """
+        raise Exception("Not implemented in PKey")
+
+    def _read_private_key_file(self, tag, filename, password=None):
+        """
+        Read an SSH2-format private key file, looking for a string of the type
+        ``"BEGIN xxx PRIVATE KEY"`` for some ``xxx``, base64-decode the text we
+        find, and return it as a string.  If the private key is encrypted and
+        ``password`` is not ``None``, the given password will be used to
+        decrypt the key (otherwise `.PasswordRequiredException` is thrown).
+
+        :param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the
+            data block.
+        :param str filename: name of the file to read.
+        :param str password:
+            an optional password to use to decrypt the key file, if it's
+            encrypted.
+        :return: the `bytes` that make up the private key.
+
+        :raises: ``IOError`` -- if there was an error reading the file.
+        :raises: `.PasswordRequiredException` -- if the private key file is
+            encrypted, and ``password`` is ``None``.
+        :raises: `.SSHException` -- if the key file is invalid.
+        """
+        with open(filename, "r") as f:
+            data = self._read_private_key(tag, f, password)
+        return data
+
+    def _read_private_key(self, tag, f, password=None):
+        lines = f.readlines()
+        if not lines:
+            raise SSHException("no lines in {} private key file".format(tag))
+
+        # find the BEGIN tag
+        start = 0
+        m = self.BEGIN_TAG.match(lines[start])
+        line_range = len(lines) - 1
+        while start < line_range and not m:
+            start += 1
+            m = self.BEGIN_TAG.match(lines[start])
+        start += 1
+        keytype = m.group(1) if m else None
+        if start >= len(lines) or keytype is None:
+            raise SSHException("not a valid {} private key file".format(tag))
+
+        # find the END tag
+        end = start
+        m = self.END_TAG.match(lines[end])
+        while end < line_range and not m:
+            end += 1
+            m = self.END_TAG.match(lines[end])
+
+        if keytype == tag:
+            data = self._read_private_key_pem(lines, end, password)
+            pkformat = self._PRIVATE_KEY_FORMAT_ORIGINAL
+        elif keytype == "OPENSSH":
+            data = self._read_private_key_openssh(lines[start:end], password)
+            pkformat = self._PRIVATE_KEY_FORMAT_OPENSSH
+        else:
+            raise SSHException(
+                "encountered {} key, expected {} key".format(keytype, tag)
+            )
+
+        return pkformat, data
+
+    def _got_bad_key_format_id(self, id_):
+        err = "{}._read_private_key() spat out an unknown key format id '{}'"
+        raise SSHException(err.format(self.__class__.__name__, id_))
+
+    def _read_private_key_pem(self, lines, end, password):
+        start = 0
+        # parse any headers first
+        headers = {}
+        start += 1
+        while start < len(lines):
+            line = lines[start].split(": ")
+            if len(line) == 1:
+                break
+            headers[line[0].lower()] = line[1].strip()
+            start += 1
+        # if we trudged to the end of the file, just try to cope.
+        try:
+            data = decodebytes(b("".join(lines[start:end])))
+        except base64.binascii.Error as e:
+            raise SSHException("base64 decoding error: {}".format(e))
+        if "proc-type" not in headers:
+            # unencryped: done
+            return data
+        # encrypted keyfile: will need a password
+        proc_type = headers["proc-type"]
+        if proc_type != "4,ENCRYPTED":
+            raise SSHException(
+                'Unknown private key structure "{}"'.format(proc_type)
+            )
+        try:
+            encryption_type, saltstr = headers["dek-info"].split(",")
+        except:
+            raise SSHException("Can't parse DEK-info in private key file")
+        if encryption_type not in self._CIPHER_TABLE:
+            raise SSHException(
+                'Unknown private key cipher "{}"'.format(encryption_type)
+            )
+        # if no password was passed in,
+        # raise an exception pointing out that we need one
+        if password is None:
+            raise PasswordRequiredException("Private key file is encrypted")
+        cipher = self._CIPHER_TABLE[encryption_type]["cipher"]
+        keysize = self._CIPHER_TABLE[encryption_type]["keysize"]
+        mode = self._CIPHER_TABLE[encryption_type]["mode"]
+        salt = unhexlify(b(saltstr))
+        key = util.generate_key_bytes(md5, salt, password, keysize)
+        decryptor = Cipher(
+            cipher(key), mode(salt), backend=default_backend()
+        ).decryptor()
+        return decryptor.update(data) + decryptor.finalize()
+
+    def _read_private_key_openssh(self, lines, password):
+        """
+        Read the new OpenSSH SSH2 private key format available
+        since OpenSSH version 6.5
+        Reference:
+        https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
+        """
+        try:
+            data = decodebytes(b("".join(lines)))
+        except base64.binascii.Error as e:
+            raise SSHException("base64 decoding error: {}".format(e))
+
+        # read data struct
+        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")
+        cipher, kdfname, kdf_options, num_pubkeys, remainder = cstruct
+        # For now, just support 1 key.
+        if num_pubkeys > 1:
+            raise SSHException(
+                "unsupported: private keyfile has multiple keys"
+            )
+        pubkey, privkey_blob = self._uint32_cstruct_unpack(remainder, "ss")
+
+        if kdfname == b("bcrypt"):
+            if cipher == b("aes256-cbc"):
+                mode = modes.CBC
+            elif cipher == b("aes256-ctr"):
+                mode = modes.CTR
+            else:
+                raise SSHException(
+                    "unknown cipher `{}` used in private key file".format(
+                        cipher.decode("utf-8")
+                    )
+                )
+            # Encrypted private key.
+            # If no password was passed in, raise an exception pointing
+            # out that we need one
+            if password is None:
+                raise PasswordRequiredException(
+                    "private key file is encrypted"
+                )
+
+            # Unpack salt and rounds from kdfoptions
+            salt, rounds = self._uint32_cstruct_unpack(kdf_options, "su")
+
+            # run bcrypt kdf to derive key and iv/nonce (32 + 16 bytes)
+            key_iv = bcrypt.kdf(
+                b(password),
+                b(salt),
+                48,
+                rounds,
+                # We can't control how many rounds are on disk, so no sense
+                # warning about it.
+                ignore_few_rounds=True,
+            )
+            key = key_iv[:32]
+            iv = key_iv[32:]
+
+            # decrypt private key blob
+            decryptor = Cipher(
+                algorithms.AES(key), mode(iv), default_backend()
+            ).decryptor()
+            decrypted_privkey = decryptor.update(privkey_blob)
+            decrypted_privkey += decryptor.finalize()
+        elif cipher == b("none") and kdfname == b("none"):
+            # Unencrypted private key
+            decrypted_privkey = privkey_blob
+        else:
+            raise SSHException(
+                "unknown cipher or kdf used in private key file"
+            )
+
+        # Unpack private key and verify checkints
+        cstruct = self._uint32_cstruct_unpack(decrypted_privkey, "uusr")
+        checkint1, checkint2, keytype, keydata = cstruct
+
+        if checkint1 != checkint2:
+            raise SSHException(
+                "OpenSSH private key file checkints do not match"
+            )
+
+        return _unpad_openssh(keydata)
+
+    def _uint32_cstruct_unpack(self, data, strformat):
+        """
+        Used to read new OpenSSH private key format.
+        Unpacks a c data structure containing a mix of 32-bit uints and
+        variable length strings prefixed by 32-bit uint size field,
+        according to the specified format. Returns the unpacked vars
+        in a tuple.
+        Format strings:
+          s - denotes a string
+          i - denotes a long integer, encoded as a byte string
+          u - denotes a 32-bit unsigned integer
+          r - the remainder of the input string, returned as a string
+        """
+        arr = []
+        idx = 0
+        try:
+            for f in strformat:
+                if f == "s":
+                    # string
+                    s_size = struct.unpack(">L", data[idx : idx + 4])[0]
+                    idx += 4
+                    s = data[idx : idx + s_size]
+                    idx += s_size
+                    arr.append(s)
+                if f == "i":
+                    # long integer
+                    s_size = struct.unpack(">L", data[idx : idx + 4])[0]
+                    idx += 4
+                    s = data[idx : idx + s_size]
+                    idx += s_size
+                    i = util.inflate_long(s, True)
+                    arr.append(i)
+                elif f == "u":
+                    # 32-bit unsigned int
+                    u = struct.unpack(">L", data[idx : idx + 4])[0]
+                    idx += 4
+                    arr.append(u)
+                elif f == "r":
+                    # remainder as string
+                    s = data[idx:]
+                    arr.append(s)
+                    break
+        except Exception as e:
+            # PKey-consuming code frequently wants to save-and-skip-over issues
+            # with loading keys, and uses SSHException as the (really friggin
+            # awful) signal for this. So for now...we do this.
+            raise SSHException(str(e))
+        return tuple(arr)
+
+    def _write_private_key_file(self, filename, key, format, password=None):
+        """
+        Write an SSH2-format private key file in a form that can be read by
+        paramiko or openssh.  If no password is given, the key is written in
+        a trivially-encoded format (base64) which is completely insecure.  If
+        a password is given, DES-EDE3-CBC is used.
+
+        :param str tag:
+            ``"RSA"`` or ``"DSA"``, the tag used to mark the data block.
+        :param filename: name of the file to write.
+        :param bytes data: data blob that makes up the private key.
+        :param str password: an optional password to use to encrypt the file.
+
+        :raises: ``IOError`` -- if there was an error writing the file.
+        """
+        # Ensure that we create new key files directly with a user-only mode,
+        # instead of opening, writing, then chmodding, which leaves us open to
+        # CVE-2022-24302.
+        with os.fdopen(
+            os.open(
+                filename,
+                # NOTE: O_TRUNC is a noop on new files, and O_CREAT is a noop
+                # on existing files, so using all 3 in both cases is fine.
+                flags=os.O_WRONLY | os.O_TRUNC | os.O_CREAT,
+                # Ditto the use of the 'mode' argument; it should be safe to
+                # give even for existing files (though it will not act like a
+                # chmod in that case).
+                mode=o600,
+            ),
+            # Yea, you still gotta inform the FLO that it is in "write" mode.
+            "w",
+        ) as f:
+            self._write_private_key(f, key, format, password=password)
+
+    def _write_private_key(self, f, key, format, password=None):
+        if password is None:
+            encryption = serialization.NoEncryption()
+        else:
+            encryption = serialization.BestAvailableEncryption(b(password))
+
+        f.write(
+            key.private_bytes(
+                serialization.Encoding.PEM, format, encryption
+            ).decode()
+        )
+
+    def _check_type_and_load_cert(self, msg, key_type, cert_type):
+        """
+        Perform message type-checking & optional certificate loading.
+
+        This includes fast-forwarding cert ``msg`` objects past the nonce, so
+        that the subsequent fields are the key numbers; thus the caller may
+        expect to treat the message as key material afterwards either way.
+
+        The obtained key type is returned for classes which need to know what
+        it was (e.g. ECDSA.)
+        """
+        # Normalization; most classes have a single key type and give a string,
+        # but eg ECDSA is a 1:N mapping.
+        key_types = key_type
+        cert_types = cert_type
+        if isinstance(key_type, str):
+            key_types = [key_types]
+        if isinstance(cert_types, str):
+            cert_types = [cert_types]
+        # Can't do much with no message, that should've been handled elsewhere
+        if msg is None:
+            raise SSHException("Key object may not be empty")
+        # First field is always key type, in either kind of object. (make sure
+        # we rewind before grabbing it - sometimes caller had to do their own
+        # introspection first!)
+        msg.rewind()
+        type_ = msg.get_text()
+        # Regular public key - nothing special to do besides the implicit
+        # type check.
+        if type_ in key_types:
+            pass
+        # OpenSSH-compatible certificate - store full copy as .public_blob
+        # (so signing works correctly) and then fast-forward past the
+        # nonce.
+        
+        elif type_ in cert_types:
+            # This seems the cleanest way to 'clone' an already-being-read
+            # message; they're *IO objects at heart and their .getvalue()
+            # always returns the full value regardless of pointer position.
+            self.load_certificate(Message(msg.asbytes()))
+            # Read out nonce as it comes before the public numbers.
+            # TODO: usefully interpret it & other non-public-number fields
+            # (requires going back into per-type subclasses.)
+            msg.get_string()
+        else:
+            err = "Invalid key (class: {}, data type: {}"
+            raise SSHException(err.format(self.__class__.__name__, type_))
+
+    def load_certificate(self, value):
+        """
+        Supplement the private key contents with data loaded from an OpenSSH
+        public key (``.pub``) or certificate (``-cert.pub``) file, a string
+        containing such a file, or a `.Message` object.
+
+        The .pub contents adds no real value, since the private key
+        file includes sufficient information to derive the public
+        key info. For certificates, however, this can be used on
+        the client side to offer authentication requests to the server
+        based on certificate instead of raw public key.
+
+        See:
+        https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys
+
+        Note: very little effort is made to validate the certificate contents,
+        that is for the server to decide if it is good enough to authenticate
+        successfully.
+        """
+        if isinstance(value, Message):
+            constructor = "from_message"
+        elif os.path.isfile(value):
+            constructor = "from_file"
+        else:
+            constructor = "from_string"
+        blob = getattr(PublicBlob, constructor)(value)
+        if not blob.key_type.startswith(self.get_name()):
+            err = "PublicBlob type {} incompatible with key type {}"
+            raise ValueError(err.format(blob.key_type, self.get_name()))
+        self.public_blob = blob
+
+
+# General construct for an OpenSSH style Public Key blob
+# readable from a one-line file of the format:
+#     <key-name> <base64-blob> [<comment>]
+# Of little value in the case of standard public keys
+# {ssh-rsa, ssh-dss, ssh-ecdsa, ssh-ed25519}, but should
+# provide rudimentary support for {*-cert.v01}
+class PublicBlob:
+    """
+    OpenSSH plain public key or OpenSSH signed public key (certificate).
+
+    Tries to be as dumb as possible and barely cares about specific
+    per-key-type data.
+
+    .. note::
+
+        Most of the time you'll want to call `from_file`, `from_string` or
+        `from_message` for useful instantiation, the main constructor is
+        basically "I should be using ``attrs`` for this."
+    """
+
+    def __init__(self, type_, blob, comment=None):
+        """
+        Create a new public blob of given type and contents.
+
+        :param str type_: Type indicator, eg ``ssh-rsa``.
+        :param bytes blob: The blob bytes themselves.
+        :param str comment: A comment, if one was given (e.g. file-based.)
+        """
+        self.key_type = type_
+        self.key_blob = blob
+        self.comment = comment
+
+    @classmethod
+    def from_file(cls, filename):
+        """
+        Create a public blob from a ``-cert.pub``-style file on disk.
+        """
+        with open(filename) as f:
+            string = f.read()
+        return cls.from_string(string)
+
+    @classmethod
+    def from_string(cls, string):
+        """
+        Create a public blob from a ``-cert.pub``-style string.
+        """
+        fields = string.split(None, 2)
+        if len(fields) < 2:
+            msg = "Not enough fields for public blob: {}"
+            raise ValueError(msg.format(fields))
+        key_type = fields[0]
+        key_blob = decodebytes(b(fields[1]))
+        try:
+            comment = fields[2].strip()
+        except IndexError:
+            comment = None
+        # Verify that the blob message first (string) field matches the
+        # key_type
+        m = Message(key_blob)
+        blob_type = m.get_text()
+        if blob_type != key_type:
+            deets = "key type={!r}, but blob type={!r}".format(
+                key_type, blob_type
+            )
+            raise ValueError("Invalid PublicBlob contents: {}".format(deets))
+        # All good? All good.
+        return cls(type_=key_type, blob=key_blob, comment=comment)
+
+    @classmethod
+    def from_message(cls, message):
+        """
+        Create a public blob from a network `.Message`.
+
+        Specifically, a cert-bearing pubkey auth packet, because by definition
+        OpenSSH-style certificates 'are' their own network representation."
+        """
+        type_ = message.get_text()
+        return cls(type_=type_, blob=message.asbytes())
+
+    def __str__(self):
+        ret = "{} public key/certificate".format(self.key_type)
+        if self.comment:
+            ret += "- {}".format(self.comment)
+        return ret
+
+    def __eq__(self, other):
+        # Just piggyback on Message/BytesIO, since both of these should be one.
+        return self and other and self.key_blob == other.key_blob
+
+    def __ne__(self, other):
+        return not self == other
diff --git a/paramiko/primes.py b/paramiko/primes.py
new file mode 100644
index 0000000..663c58e
--- /dev/null
+++ b/paramiko/primes.py
@@ -0,0 +1,148 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Utility functions for dealing with primes.
+"""
+
+import os
+
+from paramiko import util
+from paramiko.common import byte_mask
+from paramiko.ssh_exception import SSHException
+
+
+def _roll_random(n):
+    """returns a random # from 0 to N-1"""
+    bits = util.bit_length(n - 1)
+    byte_count = (bits + 7) // 8
+    hbyte_mask = pow(2, bits % 8) - 1
+
+    # so here's the plan:
+    # we fetch as many random bits as we'd need to fit N-1, and if the
+    # generated number is >= N, we try again.  in the worst case (N-1 is a
+    # power of 2), we have slightly better than 50% odds of getting one that
+    # fits, so i can't guarantee that this loop will ever finish, but the odds
+    # of it looping forever should be infinitesimal.
+    while True:
+        x = os.urandom(byte_count)
+        if hbyte_mask > 0:
+            x = byte_mask(x[0], hbyte_mask) + x[1:]
+        num = util.inflate_long(x, 1)
+        if num < n:
+            break
+    return num
+
+
+class ModulusPack:
+    """
+    convenience object for holding the contents of the /etc/ssh/moduli file,
+    on systems that have such a file.
+    """
+
+    def __init__(self):
+        # pack is a hash of: bits -> [ (generator, modulus) ... ]
+        self.pack = {}
+        self.discarded = []
+
+    def _parse_modulus(self, line):
+        (
+            timestamp,
+            mod_type,
+            tests,
+            tries,
+            size,
+            generator,
+            modulus,
+        ) = line.split()
+        mod_type = int(mod_type)
+        tests = int(tests)
+        tries = int(tries)
+        size = int(size)
+        generator = int(generator)
+        modulus = int(modulus, 16)
+
+        # weed out primes that aren't at least:
+        # type 2 (meets basic structural requirements)
+        # test 4 (more than just a small-prime sieve)
+        # tries < 100 if test & 4 (at least 100 tries of miller-rabin)
+        if (
+            mod_type < 2
+            or tests < 4
+            or (tests & 4 and tests < 8 and tries < 100)
+        ):
+            self.discarded.append(
+                (modulus, "does not meet basic requirements")
+            )
+            return
+        if generator == 0:
+            generator = 2
+
+        # there's a bug in the ssh "moduli" file (yeah, i know: shock! dismay!
+        # call cnn!) where it understates the bit lengths of these primes by 1.
+        # this is okay.
+        bl = util.bit_length(modulus)
+        if (bl != size) and (bl != size + 1):
+            self.discarded.append(
+                (modulus, "incorrectly reported bit length {}".format(size))
+            )
+            return
+        if bl not in self.pack:
+            self.pack[bl] = []
+        self.pack[bl].append((generator, modulus))
+
+    def read_file(self, filename):
+        """
+        :raises IOError: passed from any file operations that fail.
+        """
+        self.pack = {}
+        with open(filename, "r") as f:
+            for line in f:
+                line = line.strip()
+                if (len(line) == 0) or (line[0] == "#"):
+                    continue
+                try:
+                    self._parse_modulus(line)
+                except:
+                    continue
+
+    def get_modulus(self, min, prefer, max):
+        bitsizes = sorted(self.pack.keys())
+        if len(bitsizes) == 0:
+            raise SSHException("no moduli available")
+        good = -1
+        # find nearest bitsize >= preferred
+        for b in bitsizes:
+            if (b >= prefer) and (b <= max) and (b < good or good == -1):
+                good = b
+        # if that failed, find greatest bitsize >= min
+        if good == -1:
+            for b in bitsizes:
+                if (b >= min) and (b <= max) and (b > good):
+                    good = b
+        if good == -1:
+            # their entire (min, max) range has no intersection with our range.
+            # if their range is below ours, pick the smallest.  otherwise pick
+            # the largest.  it'll be out of their range requirement either way,
+            # but we'll be sending them the closest one we have.
+            good = bitsizes[0]
+            if min > good:
+                good = bitsizes[-1]
+        # now pick a random modulus of this bitsize
+        n = _roll_random(len(self.pack[good]))
+        return self.pack[good][n]
diff --git a/paramiko/proxy.py b/paramiko/proxy.py
new file mode 100644
index 0000000..f7609c9
--- /dev/null
+++ b/paramiko/proxy.py
@@ -0,0 +1,134 @@
+# Copyright (C) 2012  Yipit, Inc <coders@yipit.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+
+import os
+import shlex
+import signal
+from select import select
+import socket
+import time
+
+# Try-and-ignore import so platforms w/o subprocess (eg Google App Engine) can
+# still import paramiko.
+subprocess, subprocess_import_error = None, None
+try:
+    import subprocess
+except ImportError as e:
+    subprocess_import_error = e
+
+from paramiko.ssh_exception import ProxyCommandFailure
+from paramiko.util import ClosingContextManager
+
+
+class ProxyCommand(ClosingContextManager):
+    """
+    Wraps a subprocess running ProxyCommand-driven programs.
+
+    This class implements a the socket-like interface needed by the
+    `.Transport` and `.Packetizer` classes. Using this class instead of a
+    regular socket makes it possible to talk with a Popen'd command that will
+    proxy traffic between the client and a server hosted in another machine.
+
+    Instances of this class may be used as context managers.
+    """
+
+    def __init__(self, command_line):
+        """
+        Create a new CommandProxy instance. The instance created by this
+        class can be passed as an argument to the `.Transport` class.
+
+        :param str command_line:
+            the command that should be executed and used as the proxy.
+        """
+        if subprocess is None:
+            raise subprocess_import_error
+        self.cmd = shlex.split(command_line)
+        self.process = subprocess.Popen(
+            self.cmd,
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            bufsize=0,
+        )
+        self.timeout = None
+
+    def send(self, content):
+        """
+        Write the content received from the SSH client to the standard
+        input of the forked command.
+
+        :param str content: string to be sent to the forked command
+        """
+        try:
+            self.process.stdin.write(content)
+        except IOError as e:
+            # There was a problem with the child process. It probably
+            # died and we can't proceed. The best option here is to
+            # raise an exception informing the user that the informed
+            # ProxyCommand is not working.
+            raise ProxyCommandFailure(" ".join(self.cmd), e.strerror)
+        return len(content)
+
+    def recv(self, size):
+        """
+        Read from the standard output of the forked program.
+
+        :param int size: how many chars should be read
+
+        :return: the string of bytes read, which may be shorter than requested
+        """
+        try:
+            buffer = b""
+            start = time.time()
+            while len(buffer) < size:
+                select_timeout = None
+                if self.timeout is not None:
+                    elapsed = time.time() - start
+                    if elapsed >= self.timeout:
+                        raise socket.timeout()
+                    select_timeout = self.timeout - elapsed
+
+                r, w, x = select([self.process.stdout], [], [], select_timeout)
+                if r and r[0] == self.process.stdout:
+                    buffer += os.read(
+                        self.process.stdout.fileno(), size - len(buffer)
+                    )
+            return buffer
+        except socket.timeout:
+            if buffer:
+                # Don't raise socket.timeout, return partial result instead
+                return buffer
+            raise  # socket.timeout is a subclass of IOError
+        except IOError as e:
+            raise ProxyCommandFailure(" ".join(self.cmd), e.strerror)
+
+    def close(self):
+        os.kill(self.process.pid, signal.SIGTERM)
+
+    @property
+    def closed(self):
+        return self.process.returncode is not None
+
+    @property
+    def _closed(self):
+        # Concession to Python 3 socket-like API
+        return self.closed
+
+    def settimeout(self, timeout):
+        self.timeout = timeout
diff --git a/paramiko/rsakey.py b/paramiko/rsakey.py
new file mode 100644
index 0000000..9ea00e9
--- /dev/null
+++ b/paramiko/rsakey.py
@@ -0,0 +1,217 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+RSA keys.
+"""
+
+from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import rsa, padding
+
+from paramiko.message import Message
+from paramiko.pkey import PKey
+from paramiko.ssh_exception import SSHException
+
+
+class RSAKey(PKey):
+    """
+    Representation of an RSA key which can be used to sign and verify SSH2
+    data.
+    """
+
+    HASHES = {
+        "ssh-rsa": hashes.SHA1,
+        "ssh-rsa-cert-v01@openssh.com": hashes.SHA1,
+        "rsa-sha2-256": hashes.SHA256,
+        "rsa-sha2-256-cert-v01@openssh.com": hashes.SHA256,
+        "rsa-sha2-512": hashes.SHA512,
+        "rsa-sha2-512-cert-v01@openssh.com": hashes.SHA512,
+    }
+
+    def __init__(
+        self,
+        msg=None,
+        data=None,
+        filename=None,
+        password=None,
+        key=None,
+        file_obj=None,
+    ):
+        self.key = None
+        self.public_blob = None
+        if file_obj is not None:
+            self._from_private_key(file_obj, password)
+            return
+        if filename is not None:
+            self._from_private_key_file(filename, password)
+            return
+        if (msg is None) and (data is not None):
+            msg = Message(data)
+        if key is not None:
+            self.key = key
+        else:
+            self._check_type_and_load_cert(
+                msg=msg,
+                # NOTE: this does NOT change when using rsa2 signatures; it's
+                # purely about key loading, not exchange or verification
+                key_type="ssh-rsa",
+                cert_type="ssh-rsa-cert-v01@openssh.com",
+            )
+            self.key = rsa.RSAPublicNumbers(
+                e=msg.get_mpint(), n=msg.get_mpint()
+            ).public_key(default_backend())
+
+    @property
+    def size(self):
+        return self.key.key_size
+
+    @property
+    def public_numbers(self):
+        if isinstance(self.key, rsa.RSAPrivateKey):
+            return self.key.private_numbers().public_numbers
+        else:
+            return self.key.public_numbers()
+
+    def asbytes(self):
+        m = Message()
+        m.add_string("ssh-rsa")
+        m.add_mpint(self.public_numbers.e)
+        m.add_mpint(self.public_numbers.n)
+        return m.asbytes()
+
+    def __str__(self):
+        # NOTE: see #853 to explain some legacy behavior.
+        # TODO 4.0: replace with a nice clean fingerprint display or something
+        return self.asbytes().decode("utf8", errors="ignore")
+
+    @property
+    def _fields(self):
+        return (self.get_name(), self.public_numbers.e, self.public_numbers.n)
+
+    def get_name(self):
+        return "ssh-rsa"
+
+    def get_bits(self):
+        return self.size
+
+    def can_sign(self):
+        return isinstance(self.key, rsa.RSAPrivateKey)
+
+    def sign_ssh_data(self, data, algorithm="ssh-rsa"):
+        sig = self.key.sign(
+            data,
+            padding=padding.PKCS1v15(),
+            algorithm=self.HASHES[algorithm](),
+        )
+        m = Message()
+        m.add_string(algorithm.replace("-cert-v01@openssh.com", ""))
+        m.add_string(sig)
+        return m
+
+    def verify_ssh_sig(self, data, msg):
+        sig_algorithm = msg.get_text()
+        if sig_algorithm not in self.HASHES:
+            return False
+        key = self.key
+        if isinstance(key, rsa.RSAPrivateKey):
+            key = key.public_key()
+
+        # NOTE: pad received signature with leading zeros, key.verify()
+        # expects a signature of key size (e.g. PuTTY doesn't pad)
+        sign = msg.get_binary()
+        diff = key.key_size - len(sign) * 8
+        if diff > 0:
+            sign = b"\x00" * ((diff + 7) // 8) + sign
+
+        try:
+            key.verify(
+                sign, data, padding.PKCS1v15(), self.HASHES[sig_algorithm]()
+            )
+        except InvalidSignature:
+            return False
+        else:
+            return True
+
+    def write_private_key_file(self, filename, password=None):
+        self._write_private_key_file(
+            filename,
+            self.key,
+            serialization.PrivateFormat.TraditionalOpenSSL,
+            password=password,
+        )
+
+    def write_private_key(self, file_obj, password=None):
+        self._write_private_key(
+            file_obj,
+            self.key,
+            serialization.PrivateFormat.TraditionalOpenSSL,
+            password=password,
+        )
+
+    @staticmethod
+    def generate(bits, progress_func=None):
+        """
+        Generate a new private RSA key.  This factory function can be used to
+        generate a new host key or authentication key.
+
+        :param int bits: number of bits the generated key should be.
+        :param progress_func: Unused
+        :return: new `.RSAKey` private key
+        """
+        key = rsa.generate_private_key(
+            public_exponent=65537, key_size=bits, backend=default_backend()
+        )
+        return RSAKey(key=key)
+
+    # ...internals...
+
+    def _from_private_key_file(self, filename, password):
+        data = self._read_private_key_file("RSA", filename, password)
+        self._decode_key(data)
+
+    def _from_private_key(self, file_obj, password):
+        data = self._read_private_key("RSA", file_obj, password)
+        self._decode_key(data)
+
+    def _decode_key(self, data):
+        pkformat, data = data
+        if pkformat == self._PRIVATE_KEY_FORMAT_ORIGINAL:
+            try:
+                key = serialization.load_der_private_key(
+                    data, password=None, backend=default_backend()
+                )
+            except (ValueError, TypeError, UnsupportedAlgorithm) as e:
+                raise SSHException(str(e))
+        elif pkformat == self._PRIVATE_KEY_FORMAT_OPENSSH:
+            n, e, d, iqmp, p, q = self._uint32_cstruct_unpack(data, "iiiiii")
+            public_numbers = rsa.RSAPublicNumbers(e=e, n=n)
+            key = rsa.RSAPrivateNumbers(
+                p=p,
+                q=q,
+                d=d,
+                dmp1=d % (p - 1),
+                dmq1=d % (q - 1),
+                iqmp=iqmp,
+                public_numbers=public_numbers,
+            ).private_key(default_backend())
+        else:
+            self._got_bad_key_format_id(pkformat)
+        assert isinstance(key, rsa.RSAPrivateKey)
+        self.key = key
diff --git a/paramiko/server.py b/paramiko/server.py
new file mode 100644
index 0000000..6b0bb0f
--- /dev/null
+++ b/paramiko/server.py
@@ -0,0 +1,732 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+`.ServerInterface` is an interface to override for server support.
+"""
+
+import threading
+from paramiko import util
+from paramiko.common import (
+    DEBUG,
+    ERROR,
+    OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+    AUTH_FAILED,
+    AUTH_SUCCESSFUL,
+)
+
+
+class ServerInterface:
+    """
+    This class defines an interface for controlling the behavior of Paramiko
+    in server mode.
+
+    Methods on this class are called from Paramiko's primary thread, so you
+    shouldn't do too much work in them.  (Certainly nothing that blocks or
+    sleeps.)
+    """
+
+    def check_channel_request(self, kind, chanid):
+        """
+        Determine if a channel request of a given type will be granted, and
+        return ``OPEN_SUCCEEDED`` or an error code.  This method is
+        called in server mode when the client requests a channel, after
+        authentication is complete.
+
+        If you allow channel requests (and an ssh server that didn't would be
+        useless), you should also override some of the channel request methods
+        below, which are used to determine which services will be allowed on
+        a given channel:
+
+            - `check_channel_pty_request`
+            - `check_channel_shell_request`
+            - `check_channel_subsystem_request`
+            - `check_channel_window_change_request`
+            - `check_channel_x11_request`
+            - `check_channel_forward_agent_request`
+
+        The ``chanid`` parameter is a small number that uniquely identifies the
+        channel within a `.Transport`.  A `.Channel` object is not created
+        unless this method returns ``OPEN_SUCCEEDED`` -- once a
+        `.Channel` object is created, you can call `.Channel.get_id` to
+        retrieve the channel ID.
+
+        The return value should either be ``OPEN_SUCCEEDED`` (or
+        ``0``) to allow the channel request, or one of the following error
+        codes to reject it:
+
+            - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``
+            - ``OPEN_FAILED_CONNECT_FAILED``
+            - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``
+            - ``OPEN_FAILED_RESOURCE_SHORTAGE``
+
+        The default implementation always returns
+        ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.
+
+        :param str kind:
+            the kind of channel the client would like to open (usually
+            ``"session"``).
+        :param int chanid: ID of the channel
+        :return: an `int` success or failure code (listed above)
+        """
+        return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
+
+    def get_allowed_auths(self, username):
+        """
+        Return a list of authentication methods supported by the server.
+        This list is sent to clients attempting to authenticate, to inform them
+        of authentication methods that might be successful.
+
+        The "list" is actually a string of comma-separated names of types of
+        authentication.  Possible values are ``"password"``, ``"publickey"``,
+        and ``"none"``.
+
+        The default implementation always returns ``"password"``.
+
+        :param str username: the username requesting authentication.
+        :return: a comma-separated `str` of authentication types
+        """
+        return "password"
+
+    def check_auth_none(self, username):
+        """
+        Determine if a client may open channels with no (further)
+        authentication.
+
+        Return ``AUTH_FAILED`` if the client must authenticate, or
+        ``AUTH_SUCCESSFUL`` if it's okay for the client to not
+        authenticate.
+
+        The default implementation always returns ``AUTH_FAILED``.
+
+        :param str username: the username of the client.
+        :return:
+            ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
+            it succeeds.
+        :rtype: int
+        """
+        return AUTH_FAILED
+
+    def check_auth_password(self, username, password):
+        """
+        Determine if a given username and password supplied by the client is
+        acceptable for use in authentication.
+
+        Return ``AUTH_FAILED`` if the password is not accepted,
+        ``AUTH_SUCCESSFUL`` if the password is accepted and completes
+        the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
+        authentication is stateful, and this key is accepted for
+        authentication, but more authentication is required.  (In this latter
+        case, `get_allowed_auths` will be called to report to the client what
+        options it has for continuing the authentication.)
+
+        The default implementation always returns ``AUTH_FAILED``.
+
+        :param str username: the username of the authenticating client.
+        :param str password: the password given by the client.
+        :return:
+            ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
+            it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the password auth is
+            successful, but authentication must continue.
+        :rtype: int
+        """
+        return AUTH_FAILED
+
+    def check_auth_publickey(self, username, key):
+        """
+        Determine if a given key supplied by the client is acceptable for use
+        in authentication.  You should override this method in server mode to
+        check the username and key and decide if you would accept a signature
+        made using this key.
+
+        Return ``AUTH_FAILED`` if the key is not accepted,
+        ``AUTH_SUCCESSFUL`` if the key is accepted and completes the
+        authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
+        authentication is stateful, and this password is accepted for
+        authentication, but more authentication is required.  (In this latter
+        case, `get_allowed_auths` will be called to report to the client what
+        options it has for continuing the authentication.)
+
+        Note that you don't have to actually verify any key signtature here.
+        If you're willing to accept the key, Paramiko will do the work of
+        verifying the client's signature.
+
+        The default implementation always returns ``AUTH_FAILED``.
+
+        :param str username: the username of the authenticating client
+        :param .PKey key: the key object provided by the client
+        :return:
+            ``AUTH_FAILED`` if the client can't authenticate with this key;
+            ``AUTH_SUCCESSFUL`` if it can; ``AUTH_PARTIALLY_SUCCESSFUL`` if it
+            can authenticate with this key but must continue with
+            authentication
+        :rtype: int
+        """
+        return AUTH_FAILED
+
+    def check_auth_interactive(self, username, submethods):
+        """
+        Begin an interactive authentication challenge, if supported.  You
+        should override this method in server mode if you want to support the
+        ``"keyboard-interactive"`` auth type, which requires you to send a
+        series of questions for the client to answer.
+
+        Return ``AUTH_FAILED`` if this auth method isn't supported.  Otherwise,
+        you should return an `.InteractiveQuery` object containing the prompts
+        and instructions for the user.  The response will be sent via a call
+        to `check_auth_interactive_response`.
+
+        The default implementation always returns ``AUTH_FAILED``.
+
+        :param str username: the username of the authenticating client
+        :param str submethods:
+            a comma-separated list of methods preferred by the client (usually
+            empty)
+        :return:
+            ``AUTH_FAILED`` if this auth method isn't supported; otherwise an
+            object containing queries for the user
+        :rtype: int or `.InteractiveQuery`
+        """
+        return AUTH_FAILED
+
+    def check_auth_interactive_response(self, responses):
+        """
+        Continue or finish an interactive authentication challenge, if
+        supported.  You should override this method in server mode if you want
+        to support the ``"keyboard-interactive"`` auth type.
+
+        Return ``AUTH_FAILED`` if the responses are not accepted,
+        ``AUTH_SUCCESSFUL`` if the responses are accepted and complete
+        the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
+        authentication is stateful, and this set of responses is accepted for
+        authentication, but more authentication is required.  (In this latter
+        case, `get_allowed_auths` will be called to report to the client what
+        options it has for continuing the authentication.)
+
+        If you wish to continue interactive authentication with more questions,
+        you may return an `.InteractiveQuery` object, which should cause the
+        client to respond with more answers, calling this method again.  This
+        cycle can continue indefinitely.
+
+        The default implementation always returns ``AUTH_FAILED``.
+
+        :param responses: list of `str` responses from the client
+        :return:
+            ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
+            it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the interactive auth
+            is successful, but authentication must continue; otherwise an
+            object containing queries for the user
+        :rtype: int or `.InteractiveQuery`
+        """
+        return AUTH_FAILED
+
+    def check_auth_gssapi_with_mic(
+        self, username, gss_authenticated=AUTH_FAILED, cc_file=None
+    ):
+        """
+        Authenticate the given user to the server if he is a valid krb5
+        principal.
+
+        :param str username: The username of the authenticating client
+        :param int gss_authenticated: The result of the krb5 authentication
+        :param str cc_filename: The krb5 client credentials cache filename
+        :return: ``AUTH_FAILED`` if the user is not authenticated otherwise
+                 ``AUTH_SUCCESSFUL``
+        :rtype: int
+        :note: Kerberos credential delegation is not supported.
+        :see: `.ssh_gss`
+        :note: : We are just checking in L{AuthHandler} that the given user is
+                 a valid krb5 principal!
+                 We don't check if the krb5 principal is allowed to log in on
+                 the server, because there is no way to do that in python. So
+                 if you develop your own SSH server with paramiko for a cetain
+                 platform like Linux, you should call C{krb5_kuserok()} in
+                 your local kerberos library to make sure that the
+                 krb5_principal has an account on the server and is allowed to
+                 log in as a user.
+        :see: http://www.unix.com/man-page/all/3/krb5_kuserok/
+        """
+        if gss_authenticated == AUTH_SUCCESSFUL:
+            return AUTH_SUCCESSFUL
+        return AUTH_FAILED
+
+    def check_auth_gssapi_keyex(
+        self, username, gss_authenticated=AUTH_FAILED, cc_file=None
+    ):
+        """
+        Authenticate the given user to the server if he is a valid krb5
+        principal and GSS-API Key Exchange was performed.
+        If GSS-API Key Exchange was not performed, this authentication method
+        won't be available.
+
+        :param str username: The username of the authenticating client
+        :param int gss_authenticated: The result of the krb5 authentication
+        :param str cc_filename: The krb5 client credentials cache filename
+        :return: ``AUTH_FAILED`` if the user is not authenticated otherwise
+                 ``AUTH_SUCCESSFUL``
+        :rtype: int
+        :note: Kerberos credential delegation is not supported.
+        :see: `.ssh_gss` `.kex_gss`
+        :note: : We are just checking in L{AuthHandler} that the given user is
+                 a valid krb5 principal!
+                 We don't check if the krb5 principal is allowed to log in on
+                 the server, because there is no way to do that in python. So
+                 if you develop your own SSH server with paramiko for a cetain
+                 platform like Linux, you should call C{krb5_kuserok()} in
+                 your local kerberos library to make sure that the
+                 krb5_principal has an account on the server and is allowed
+                 to log in as a user.
+        :see: http://www.unix.com/man-page/all/3/krb5_kuserok/
+        """
+        if gss_authenticated == AUTH_SUCCESSFUL:
+            return AUTH_SUCCESSFUL
+        return AUTH_FAILED
+
+    def enable_auth_gssapi(self):
+        """
+        Overwrite this function in your SSH server to enable GSSAPI
+        authentication.
+        The default implementation always returns false.
+
+        :returns bool: Whether GSSAPI authentication is enabled.
+        :see: `.ssh_gss`
+        """
+        UseGSSAPI = False
+        return UseGSSAPI
+
+    def check_port_forward_request(self, address, port):
+        """
+        Handle a request for port forwarding.  The client is asking that
+        connections to the given address and port be forwarded back across
+        this ssh connection.  An address of ``"0.0.0.0"`` indicates a global
+        address (any address associated with this server) and a port of ``0``
+        indicates that no specific port is requested (usually the OS will pick
+        a port).
+
+        The default implementation always returns ``False``, rejecting the
+        port forwarding request.  If the request is accepted, you should return
+        the port opened for listening.
+
+        :param str address: the requested address
+        :param int port: the requested port
+        :return:
+            the port number (`int`) that was opened for listening, or ``False``
+            to reject
+        """
+        return False
+
+    def cancel_port_forward_request(self, address, port):
+        """
+        The client would like to cancel a previous port-forwarding request.
+        If the given address and port is being forwarded across this ssh
+        connection, the port should be closed.
+
+        :param str address: the forwarded address
+        :param int port: the forwarded port
+        """
+        pass
+
+    def check_global_request(self, kind, msg):
+        """
+        Handle a global request of the given ``kind``.  This method is called
+        in server mode and client mode, whenever the remote host makes a global
+        request.  If there are any arguments to the request, they will be in
+        ``msg``.
+
+        There aren't any useful global requests defined, aside from port
+        forwarding, so usually this type of request is an extension to the
+        protocol.
+
+        If the request was successful and you would like to return contextual
+        data to the remote host, return a tuple.  Items in the tuple will be
+        sent back with the successful result.  (Note that the items in the
+        tuple can only be strings, ints, or bools.)
+
+        The default implementation always returns ``False``, indicating that it
+        does not support any global requests.
+
+        .. note:: Port forwarding requests are handled separately, in
+            `check_port_forward_request`.
+
+        :param str kind: the kind of global request being made.
+        :param .Message msg: any extra arguments to the request.
+        :return:
+            ``True`` or a `tuple` of data if the request was granted; ``False``
+            otherwise.
+        """
+        return False
+
+    # ...Channel requests...
+
+    def check_channel_pty_request(
+        self, channel, term, width, height, pixelwidth, pixelheight, modes
+    ):
+        """
+        Determine if a pseudo-terminal of the given dimensions (usually
+        requested for shell access) can be provided on the given channel.
+
+        The default implementation always returns ``False``.
+
+        :param .Channel channel: the `.Channel` the pty request arrived on.
+        :param str term: type of terminal requested (for example, ``"vt100"``).
+        :param int width: width of screen in characters.
+        :param int height: height of screen in characters.
+        :param int pixelwidth:
+            width of screen in pixels, if known (may be ``0`` if unknown).
+        :param int pixelheight:
+            height of screen in pixels, if known (may be ``0`` if unknown).
+        :return:
+            ``True`` if the pseudo-terminal has been allocated; ``False``
+            otherwise.
+        """
+        return False
+
+    def check_channel_shell_request(self, channel):
+        """
+        Determine if a shell will be provided to the client on the given
+        channel.  If this method returns ``True``, the channel should be
+        connected to the stdin/stdout of a shell (or something that acts like
+        a shell).
+
+        The default implementation always returns ``False``.
+
+        :param .Channel channel: the `.Channel` the request arrived on.
+        :return:
+            ``True`` if this channel is now hooked up to a shell; ``False`` if
+            a shell can't or won't be provided.
+        """
+        return False
+
+    def check_channel_exec_request(self, channel, command):
+        """
+        Determine if a shell command will be executed for the client.  If this
+        method returns ``True``, the channel should be connected to the stdin,
+        stdout, and stderr of the shell command.
+
+        The default implementation always returns ``False``.
+
+        :param .Channel channel: the `.Channel` the request arrived on.
+        :param str command: the command to execute.
+        :return:
+            ``True`` if this channel is now hooked up to the stdin, stdout, and
+            stderr of the executing command; ``False`` if the command will not
+            be executed.
+
+        .. versionadded:: 1.1
+        """
+        return False
+
+    def check_channel_subsystem_request(self, channel, name):
+        """
+        Determine if a requested subsystem will be provided to the client on
+        the given channel.  If this method returns ``True``, all future I/O
+        through this channel will be assumed to be connected to the requested
+        subsystem.  An example of a subsystem is ``sftp``.
+
+        The default implementation checks for a subsystem handler assigned via
+        `.Transport.set_subsystem_handler`.
+        If one has been set, the handler is invoked and this method returns
+        ``True``.  Otherwise it returns ``False``.
+
+        .. note:: Because the default implementation uses the `.Transport` to
+            identify valid subsystems, you probably won't need to override this
+            method.
+
+        :param .Channel channel: the `.Channel` the pty request arrived on.
+        :param str name: name of the requested subsystem.
+        :return:
+            ``True`` if this channel is now hooked up to the requested
+            subsystem; ``False`` if that subsystem can't or won't be provided.
+        """
+        transport = channel.get_transport()
+        handler_class, args, kwargs = transport._get_subsystem_handler(name)
+        if handler_class is None:
+            return False
+        handler = handler_class(channel, name, self, *args, **kwargs)
+        handler.start()
+        return True
+
+    def check_channel_window_change_request(
+        self, channel, width, height, pixelwidth, pixelheight
+    ):
+        """
+        Determine if the pseudo-terminal on the given channel can be resized.
+        This only makes sense if a pty was previously allocated on it.
+
+        The default implementation always returns ``False``.
+
+        :param .Channel channel: the `.Channel` the pty request arrived on.
+        :param int width: width of screen in characters.
+        :param int height: height of screen in characters.
+        :param int pixelwidth:
+            width of screen in pixels, if known (may be ``0`` if unknown).
+        :param int pixelheight:
+            height of screen in pixels, if known (may be ``0`` if unknown).
+        :return: ``True`` if the terminal was resized; ``False`` if not.
+        """
+        return False
+
+    def check_channel_x11_request(
+        self,
+        channel,
+        single_connection,
+        auth_protocol,
+        auth_cookie,
+        screen_number,
+    ):
+        """
+        Determine if the client will be provided with an X11 session.  If this
+        method returns ``True``, X11 applications should be routed through new
+        SSH channels, using `.Transport.open_x11_channel`.
+
+        The default implementation always returns ``False``.
+
+        :param .Channel channel: the `.Channel` the X11 request arrived on
+        :param bool single_connection:
+            ``True`` if only a single X11 channel should be opened, else
+            ``False``.
+        :param str auth_protocol: the protocol used for X11 authentication
+        :param str auth_cookie: the cookie used to authenticate to X11
+        :param int screen_number: the number of the X11 screen to connect to
+        :return: ``True`` if the X11 session was opened; ``False`` if not
+        """
+        return False
+
+    def check_channel_forward_agent_request(self, channel):
+        """
+        Determine if the client will be provided with an forward agent session.
+        If this method returns ``True``, the server will allow SSH Agent
+        forwarding.
+
+        The default implementation always returns ``False``.
+
+        :param .Channel channel: the `.Channel` the request arrived on
+        :return: ``True`` if the AgentForward was loaded; ``False`` if not
+
+        If ``True`` is returned, the server should create an
+        :class:`AgentServerProxy` to access the agent.
+        """
+        return False
+
+    def check_channel_direct_tcpip_request(self, chanid, origin, destination):
+        """
+        Determine if a local port forwarding channel will be granted, and
+        return ``OPEN_SUCCEEDED`` or an error code.  This method is
+        called in server mode when the client requests a channel, after
+        authentication is complete.
+
+        The ``chanid`` parameter is a small number that uniquely identifies the
+        channel within a `.Transport`.  A `.Channel` object is not created
+        unless this method returns ``OPEN_SUCCEEDED`` -- once a
+        `.Channel` object is created, you can call `.Channel.get_id` to
+        retrieve the channel ID.
+
+        The origin and destination parameters are (ip_address, port) tuples
+        that correspond to both ends of the TCP connection in the forwarding
+        tunnel.
+
+        The return value should either be ``OPEN_SUCCEEDED`` (or
+        ``0``) to allow the channel request, or one of the following error
+        codes to reject it:
+
+            - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``
+            - ``OPEN_FAILED_CONNECT_FAILED``
+            - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``
+            - ``OPEN_FAILED_RESOURCE_SHORTAGE``
+
+        The default implementation always returns
+        ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.
+
+        :param int chanid: ID of the channel
+        :param tuple origin:
+            2-tuple containing the IP address and port of the originator
+            (client side)
+        :param tuple destination:
+            2-tuple containing the IP address and port of the destination
+            (server side)
+        :return: an `int` success or failure code (listed above)
+        """
+        return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
+
+    def check_channel_env_request(self, channel, name, value):
+        """
+        Check whether a given environment variable can be specified for the
+        given channel.  This method should return ``True`` if the server
+        is willing to set the specified environment variable.  Note that
+        some environment variables (e.g., PATH) can be exceedingly
+        dangerous, so blindly allowing the client to set the environment
+        is almost certainly not a good idea.
+
+        The default implementation always returns ``False``.
+
+        :param channel: the `.Channel` the env request arrived on
+        :param str name: name
+        :param str value: Channel value
+        :returns: A boolean
+        """
+        return False
+
+    def get_banner(self):
+        """
+        A pre-login banner to display to the user. The message may span
+        multiple lines separated by crlf pairs. The language should be in
+        rfc3066 style, for example: en-US
+
+        The default implementation always returns ``(None, None)``.
+
+        :returns: A tuple containing the banner and language code.
+
+        .. versionadded:: 2.3
+        """
+        return (None, None)
+
+
+class InteractiveQuery:
+    """
+    A query (set of prompts) for a user during interactive authentication.
+    """
+
+    def __init__(self, name="", instructions="", *prompts):
+        """
+        Create a new interactive query to send to the client.  The name and
+        instructions are optional, but are generally displayed to the end
+        user.  A list of prompts may be included, or they may be added via
+        the `add_prompt` method.
+
+        :param str name: name of this query
+        :param str instructions:
+            user instructions (usually short) about this query
+        :param str prompts: one or more authentication prompts
+        """
+        self.name = name
+        self.instructions = instructions
+        self.prompts = []
+        for x in prompts:
+            if isinstance(x, str):
+                self.add_prompt(x)
+            else:
+                self.add_prompt(x[0], x[1])
+
+    def add_prompt(self, prompt, echo=True):
+        """
+        Add a prompt to this query.  The prompt should be a (reasonably short)
+        string.  Multiple prompts can be added to the same query.
+
+        :param str prompt: the user prompt
+        :param bool echo:
+            ``True`` (default) if the user's response should be echoed;
+            ``False`` if not (for a password or similar)
+        """
+        self.prompts.append((prompt, echo))
+
+
+class SubsystemHandler(threading.Thread):
+    """
+    Handler for a subsystem in server mode.  If you create a subclass of this
+    class and pass it to `.Transport.set_subsystem_handler`, an object of this
+    class will be created for each request for this subsystem.  Each new object
+    will be executed within its own new thread by calling `start_subsystem`.
+    When that method completes, the channel is closed.
+
+    For example, if you made a subclass ``MP3Handler`` and registered it as the
+    handler for subsystem ``"mp3"``, then whenever a client has successfully
+    authenticated and requests subsystem ``"mp3"``, an object of class
+    ``MP3Handler`` will be created, and `start_subsystem` will be called on
+    it from a new thread.
+    """
+
+    def __init__(self, channel, name, server):
+        """
+        Create a new handler for a channel.  This is used by `.ServerInterface`
+        to start up a new handler when a channel requests this subsystem.  You
+        don't need to override this method, but if you do, be sure to pass the
+        ``channel`` and ``name`` parameters through to the original
+        ``__init__`` method here.
+
+        :param .Channel channel: the channel associated with this
+            subsystem request.
+        :param str name: name of the requested subsystem.
+        :param .ServerInterface server:
+            the server object for the session that started this subsystem
+        """
+        threading.Thread.__init__(self, target=self._run)
+        self.__channel = channel
+        self.__transport = channel.get_transport()
+        self.__name = name
+        self.__server = server
+
+    def get_server(self):
+        """
+        Return the `.ServerInterface` object associated with this channel and
+        subsystem.
+        """
+        return self.__server
+
+    def _run(self):
+        try:
+            self.__transport._log(
+                DEBUG, "Starting handler for subsystem {}".format(self.__name)
+            )
+            self.start_subsystem(self.__name, self.__transport, self.__channel)
+        except Exception as e:
+            self.__transport._log(
+                ERROR,
+                'Exception in subsystem handler for "{}": {}'.format(
+                    self.__name, e
+                ),
+            )
+            self.__transport._log(ERROR, util.tb_strings())
+        try:
+            self.finish_subsystem()
+        except:
+            pass
+
+    def start_subsystem(self, name, transport, channel):
+        """
+        Process an ssh subsystem in server mode.  This method is called on a
+        new object (and in a new thread) for each subsystem request.  It is
+        assumed that all subsystem logic will take place here, and when the
+        subsystem is finished, this method will return.  After this method
+        returns, the channel is closed.
+
+        The combination of ``transport`` and ``channel`` are unique; this
+        handler corresponds to exactly one `.Channel` on one `.Transport`.
+
+        .. note::
+            It is the responsibility of this method to exit if the underlying
+            `.Transport` is closed.  This can be done by checking
+            `.Transport.is_active` or noticing an EOF on the `.Channel`.  If
+            this method loops forever without checking for this case, your
+            Python interpreter may refuse to exit because this thread will
+            still be running.
+
+        :param str name: name of the requested subsystem.
+        :param .Transport transport: the server-mode `.Transport`.
+        :param .Channel channel: the channel associated with this subsystem
+            request.
+        """
+        pass
+
+    def finish_subsystem(self):
+        """
+        Perform any cleanup at the end of a subsystem.  The default
+        implementation just closes the channel.
+
+        .. versionadded:: 1.1
+        """
+        self.__channel.close()
diff --git a/paramiko/sftp.py b/paramiko/sftp.py
new file mode 100644
index 0000000..b3528d4
--- /dev/null
+++ b/paramiko/sftp.py
@@ -0,0 +1,224 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+import select
+import socket
+import struct
+
+from paramiko import util
+from paramiko.common import DEBUG, byte_chr, byte_ord
+from paramiko.message import Message
+
+
+(
+    CMD_INIT,
+    CMD_VERSION,
+    CMD_OPEN,
+    CMD_CLOSE,
+    CMD_READ,
+    CMD_WRITE,
+    CMD_LSTAT,
+    CMD_FSTAT,
+    CMD_SETSTAT,
+    CMD_FSETSTAT,
+    CMD_OPENDIR,
+    CMD_READDIR,
+    CMD_REMOVE,
+    CMD_MKDIR,
+    CMD_RMDIR,
+    CMD_REALPATH,
+    CMD_STAT,
+    CMD_RENAME,
+    CMD_READLINK,
+    CMD_SYMLINK,
+) = range(1, 21)
+(CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS) = range(101, 106)
+(CMD_EXTENDED, CMD_EXTENDED_REPLY) = range(200, 202)
+
+SFTP_OK = 0
+(
+    SFTP_EOF,
+    SFTP_NO_SUCH_FILE,
+    SFTP_PERMISSION_DENIED,
+    SFTP_FAILURE,
+    SFTP_BAD_MESSAGE,
+    SFTP_NO_CONNECTION,
+    SFTP_CONNECTION_LOST,
+    SFTP_OP_UNSUPPORTED,
+) = range(1, 9)
+
+SFTP_DESC = [
+    "Success",
+    "End of file",
+    "No such file",
+    "Permission denied",
+    "Failure",
+    "Bad message",
+    "No connection",
+    "Connection lost",
+    "Operation unsupported",
+]
+
+SFTP_FLAG_READ = 0x1
+SFTP_FLAG_WRITE = 0x2
+SFTP_FLAG_APPEND = 0x4
+SFTP_FLAG_CREATE = 0x8
+SFTP_FLAG_TRUNC = 0x10
+SFTP_FLAG_EXCL = 0x20
+
+_VERSION = 3
+
+
+# for debugging
+CMD_NAMES = {
+    CMD_INIT: "init",
+    CMD_VERSION: "version",
+    CMD_OPEN: "open",
+    CMD_CLOSE: "close",
+    CMD_READ: "read",
+    CMD_WRITE: "write",
+    CMD_LSTAT: "lstat",
+    CMD_FSTAT: "fstat",
+    CMD_SETSTAT: "setstat",
+    CMD_FSETSTAT: "fsetstat",
+    CMD_OPENDIR: "opendir",
+    CMD_READDIR: "readdir",
+    CMD_REMOVE: "remove",
+    CMD_MKDIR: "mkdir",
+    CMD_RMDIR: "rmdir",
+    CMD_REALPATH: "realpath",
+    CMD_STAT: "stat",
+    CMD_RENAME: "rename",
+    CMD_READLINK: "readlink",
+    CMD_SYMLINK: "symlink",
+    CMD_STATUS: "status",
+    CMD_HANDLE: "handle",
+    CMD_DATA: "data",
+    CMD_NAME: "name",
+    CMD_ATTRS: "attrs",
+    CMD_EXTENDED: "extended",
+    CMD_EXTENDED_REPLY: "extended_reply",
+}
+
+
+# TODO: rewrite SFTP file/server modules' overly-flexible "make a request with
+# xyz components" so we don't need this very silly method of signaling whether
+# a given Python integer should be 32- or 64-bit.
+# NOTE: this only became an issue when dropping Python 2 support; prior to
+# doing so, we had to support actual-longs, which served as that signal. This
+# is simply recreating that structure in a more tightly scoped fashion.
+class int64(int):
+    pass
+
+
+class SFTPError(Exception):
+    pass
+
+
+class BaseSFTP:
+    def __init__(self):
+        self.logger = util.get_logger("paramiko.sftp")
+        self.sock = None
+        self.ultra_debug = False
+
+    # ...internals...
+
+    def _send_version(self):
+        m = Message()
+        m.add_int(_VERSION)
+        self._send_packet(CMD_INIT, m)
+        t, data = self._read_packet()
+        if t != CMD_VERSION:
+            raise SFTPError("Incompatible sftp protocol")
+        version = struct.unpack(">I", data[:4])[0]
+        #        if version != _VERSION:
+        #            raise SFTPError('Incompatible sftp protocol')
+        return version
+
+    def _send_server_version(self):
+        # winscp will freak out if the server sends version info before the
+        # client finishes sending INIT.
+        t, data = self._read_packet()
+        if t != CMD_INIT:
+            raise SFTPError("Incompatible sftp protocol")
+        version = struct.unpack(">I", data[:4])[0]
+        # advertise that we support "check-file"
+        extension_pairs = ["check-file", "md5,sha1"]
+        msg = Message()
+        msg.add_int(_VERSION)
+        msg.add(*extension_pairs)
+        self._send_packet(CMD_VERSION, msg)
+        return version
+
+    def _log(self, level, msg, *args):
+        self.logger.log(level, msg, *args)
+
+    def _write_all(self, out):
+        while len(out) > 0:
+            n = self.sock.send(out)
+            if n <= 0:
+                raise EOFError()
+            if n == len(out):
+                return
+            out = out[n:]
+        return
+
+    def _read_all(self, n):
+        out = bytes()
+        while n > 0:
+            if isinstance(self.sock, socket.socket):
+                # sometimes sftp is used directly over a socket instead of
+                # through a paramiko channel.  in this case, check periodically
+                # if the socket is closed.  (for some reason, recv() won't ever
+                # return or raise an exception, but calling select on a closed
+                # socket will.)
+                while True:
+                    read, write, err = select.select([self.sock], [], [], 0.1)
+                    if len(read) > 0:
+                        x = self.sock.recv(n)
+                        break
+            else:
+                x = self.sock.recv(n)
+
+            if len(x) == 0:
+                raise EOFError()
+            out += x
+            n -= len(x)
+        return out
+
+    def _send_packet(self, t, packet):
+        packet = packet.asbytes()
+        out = struct.pack(">I", len(packet) + 1) + byte_chr(t) + packet
+        if self.ultra_debug:
+            self._log(DEBUG, util.format_binary(out, "OUT: "))
+        self._write_all(out)
+
+    def _read_packet(self):
+        x = self._read_all(4)
+        # most sftp servers won't accept packets larger than about 32k, so
+        # anything with the high byte set (> 16MB) is just garbage.
+        if byte_ord(x[0]):
+            raise SFTPError("Garbage packet received")
+        size = struct.unpack(">I", x)[0]
+        data = self._read_all(size)
+        if self.ultra_debug:
+            self._log(DEBUG, util.format_binary(data, "IN: "))
+        if size > 0:
+            t = byte_ord(data[0])
+            return t, data[1:]
+        return 0, bytes()
diff --git a/paramiko/sftp_attr.py b/paramiko/sftp_attr.py
new file mode 100644
index 0000000..18ffbf8
--- /dev/null
+++ b/paramiko/sftp_attr.py
@@ -0,0 +1,239 @@
+# Copyright (C) 2003-2006 Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+import stat
+import time
+from paramiko.common import x80000000, o700, o70, xffffffff
+
+
+class SFTPAttributes:
+    """
+    Representation of the attributes of a file (or proxied file) for SFTP in
+    client or server mode.  It attempts to mirror the object returned by
+    `os.stat` as closely as possible, so it may have the following fields,
+    with the same meanings as those returned by an `os.stat` object:
+
+        - ``st_size``
+        - ``st_uid``
+        - ``st_gid``
+        - ``st_mode``
+        - ``st_atime``
+        - ``st_mtime``
+
+    Because SFTP allows flags to have other arbitrary named attributes, these
+    are stored in a dict named ``attr``.  Occasionally, the filename is also
+    stored, in ``filename``.
+    """
+
+    FLAG_SIZE = 1
+    FLAG_UIDGID = 2
+    FLAG_PERMISSIONS = 4
+    FLAG_AMTIME = 8
+    FLAG_EXTENDED = x80000000
+
+    def __init__(self):
+        """
+        Create a new (empty) SFTPAttributes object.  All fields will be empty.
+        """
+        self._flags = 0
+        self.st_size = None
+        self.st_uid = None
+        self.st_gid = None
+        self.st_mode = None
+        self.st_atime = None
+        self.st_mtime = None
+        self.attr = {}
+
+    @classmethod
+    def from_stat(cls, obj, filename=None):
+        """
+        Create an `.SFTPAttributes` object from an existing ``stat`` object (an
+        object returned by `os.stat`).
+
+        :param object obj: an object returned by `os.stat` (or equivalent).
+        :param str filename: the filename associated with this file.
+        :return: new `.SFTPAttributes` object with the same attribute fields.
+        """
+        attr = cls()
+        attr.st_size = obj.st_size
+        attr.st_uid = obj.st_uid
+        attr.st_gid = obj.st_gid
+        attr.st_mode = obj.st_mode
+        attr.st_atime = obj.st_atime
+        attr.st_mtime = obj.st_mtime
+        if filename is not None:
+            attr.filename = filename
+        return attr
+
+    def __repr__(self):
+        return "<SFTPAttributes: {}>".format(self._debug_str())
+
+    # ...internals...
+    @classmethod
+    def _from_msg(cls, msg, filename=None, longname=None):
+        attr = cls()
+        attr._unpack(msg)
+        if filename is not None:
+            attr.filename = filename
+        if longname is not None:
+            attr.longname = longname
+        return attr
+
+    def _unpack(self, msg):
+        self._flags = msg.get_int()
+        if self._flags & self.FLAG_SIZE:
+            self.st_size = msg.get_int64()
+        if self._flags & self.FLAG_UIDGID:
+            self.st_uid = msg.get_int()
+            self.st_gid = msg.get_int()
+        if self._flags & self.FLAG_PERMISSIONS:
+            self.st_mode = msg.get_int()
+        if self._flags & self.FLAG_AMTIME:
+            self.st_atime = msg.get_int()
+            self.st_mtime = msg.get_int()
+        if self._flags & self.FLAG_EXTENDED:
+            count = msg.get_int()
+            for i in range(count):
+                self.attr[msg.get_string()] = msg.get_string()
+
+    def _pack(self, msg):
+        self._flags = 0
+        if self.st_size is not None:
+            self._flags |= self.FLAG_SIZE
+        if (self.st_uid is not None) and (self.st_gid is not None):
+            self._flags |= self.FLAG_UIDGID
+        if self.st_mode is not None:
+            self._flags |= self.FLAG_PERMISSIONS
+        if (self.st_atime is not None) and (self.st_mtime is not None):
+            self._flags |= self.FLAG_AMTIME
+        if len(self.attr) > 0:
+            self._flags |= self.FLAG_EXTENDED
+        msg.add_int(self._flags)
+        if self._flags & self.FLAG_SIZE:
+            msg.add_int64(self.st_size)
+        if self._flags & self.FLAG_UIDGID:
+            msg.add_int(self.st_uid)
+            msg.add_int(self.st_gid)
+        if self._flags & self.FLAG_PERMISSIONS:
+            msg.add_int(self.st_mode)
+        if self._flags & self.FLAG_AMTIME:
+            # throw away any fractional seconds
+            msg.add_int(int(self.st_atime))
+            msg.add_int(int(self.st_mtime))
+        if self._flags & self.FLAG_EXTENDED:
+            msg.add_int(len(self.attr))
+            for key, val in self.attr.items():
+                msg.add_string(key)
+                msg.add_string(val)
+        return
+
+    def _debug_str(self):
+        out = "[ "
+        if self.st_size is not None:
+            out += "size={} ".format(self.st_size)
+        if (self.st_uid is not None) and (self.st_gid is not None):
+            out += "uid={} gid={} ".format(self.st_uid, self.st_gid)
+        if self.st_mode is not None:
+            out += "mode=" + oct(self.st_mode) + " "
+        if (self.st_atime is not None) and (self.st_mtime is not None):
+            out += "atime={} mtime={} ".format(self.st_atime, self.st_mtime)
+        for k, v in self.attr.items():
+            out += '"{}"={!r} '.format(str(k), v)
+        out += "]"
+        return out
+
+    @staticmethod
+    def _rwx(n, suid, sticky=False):
+        if suid:
+            suid = 2
+        out = "-r"[n >> 2] + "-w"[(n >> 1) & 1]
+        if sticky:
+            out += "-xTt"[suid + (n & 1)]
+        else:
+            out += "-xSs"[suid + (n & 1)]
+        return out
+
+    def __str__(self):
+        """create a unix-style long description of the file (like ls -l)"""
+        if self.st_mode is not None:
+            kind = stat.S_IFMT(self.st_mode)
+            if kind == stat.S_IFIFO:
+                ks = "p"
+            elif kind == stat.S_IFCHR:
+                ks = "c"
+            elif kind == stat.S_IFDIR:
+                ks = "d"
+            elif kind == stat.S_IFBLK:
+                ks = "b"
+            elif kind == stat.S_IFREG:
+                ks = "-"
+            elif kind == stat.S_IFLNK:
+                ks = "l"
+            elif kind == stat.S_IFSOCK:
+                ks = "s"
+            else:
+                ks = "?"
+            ks += self._rwx(
+                (self.st_mode & o700) >> 6, self.st_mode & stat.S_ISUID
+            )
+            ks += self._rwx(
+                (self.st_mode & o70) >> 3, self.st_mode & stat.S_ISGID
+            )
+            ks += self._rwx(
+                self.st_mode & 7, self.st_mode & stat.S_ISVTX, True
+            )
+        else:
+            ks = "?---------"
+        # compute display date
+        if (self.st_mtime is None) or (self.st_mtime == xffffffff):
+            # shouldn't really happen
+            datestr = "(unknown date)"
+        else:
+            time_tuple = time.localtime(self.st_mtime)
+            if abs(time.time() - self.st_mtime) > 15_552_000:
+                # (15,552,000s = 6 months)
+                datestr = time.strftime("%d %b %Y", time_tuple)
+            else:
+                datestr = time.strftime("%d %b %H:%M", time_tuple)
+        filename = getattr(self, "filename", "?")
+
+        # not all servers support uid/gid
+        uid = self.st_uid
+        gid = self.st_gid
+        size = self.st_size
+        if uid is None:
+            uid = 0
+        if gid is None:
+            gid = 0
+        if size is None:
+            size = 0
+
+        # TODO: not sure this actually worked as expected beforehand, leaving
+        # it untouched for the time being, re: .format() upgrade, until someone
+        # has time to doublecheck
+        return "%s   1 %-8d %-8d %8d %-12s %s" % (
+            ks,
+            uid,
+            gid,
+            size,
+            datestr,
+            filename,
+        )
+
+    def asbytes(self):
+        return str(self).encode()
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
new file mode 100644
index 0000000..31ac129
--- /dev/null
+++ b/paramiko/sftp_client.py
@@ -0,0 +1,930 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of Paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+
+from binascii import hexlify
+import errno
+import os
+import stat
+import threading
+import time
+import weakref
+from paramiko import util
+from paramiko.channel import Channel
+from paramiko.message import Message
+from paramiko.common import INFO, DEBUG, o777
+from paramiko.sftp import (
+    BaseSFTP,
+    CMD_OPENDIR,
+    CMD_HANDLE,
+    SFTPError,
+    CMD_READDIR,
+    CMD_NAME,
+    CMD_CLOSE,
+    SFTP_FLAG_READ,
+    SFTP_FLAG_WRITE,
+    SFTP_FLAG_CREATE,
+    SFTP_FLAG_TRUNC,
+    SFTP_FLAG_APPEND,
+    SFTP_FLAG_EXCL,
+    CMD_OPEN,
+    CMD_REMOVE,
+    CMD_RENAME,
+    CMD_MKDIR,
+    CMD_RMDIR,
+    CMD_STAT,
+    CMD_ATTRS,
+    CMD_LSTAT,
+    CMD_SYMLINK,
+    CMD_SETSTAT,
+    CMD_READLINK,
+    CMD_REALPATH,
+    CMD_STATUS,
+    CMD_EXTENDED,
+    SFTP_OK,
+    SFTP_EOF,
+    SFTP_NO_SUCH_FILE,
+    SFTP_PERMISSION_DENIED,
+    int64,
+)
+
+from paramiko.sftp_attr import SFTPAttributes
+from paramiko.ssh_exception import SSHException
+from paramiko.sftp_file import SFTPFile
+from paramiko.util import ClosingContextManager, b, u
+
+
+def _to_unicode(s):
+    """
+    decode a string as ascii or utf8 if possible (as required by the sftp
+    protocol).  if neither works, just return a byte string because the server
+    probably doesn't know the filename's encoding.
+    """
+    try:
+        return s.encode("ascii")
+    except (UnicodeError, AttributeError):
+        try:
+            return s.decode("utf-8")
+        except UnicodeError:
+            return s
+
+
+b_slash = b"/"
+
+
+class SFTPClient(BaseSFTP, ClosingContextManager):
+    """
+    SFTP client object.
+
+    Used to open an SFTP session across an open SSH `.Transport` and perform
+    remote file operations.
+
+    Instances of this class may be used as context managers.
+    """
+
+    def __init__(self, sock):
+        """
+        Create an SFTP client from an existing `.Channel`.  The channel
+        should already have requested the ``"sftp"`` subsystem.
+
+        An alternate way to create an SFTP client context is by using
+        `from_transport`.
+
+        :param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem
+
+        :raises:
+            `.SSHException` -- if there's an exception while negotiating sftp
+        """
+        BaseSFTP.__init__(self)
+        self.sock = sock
+        self.ultra_debug = False
+        self.request_number = 1
+        # lock for request_number
+        self._lock = threading.Lock()
+        self._cwd = None
+        # request # -> SFTPFile
+        self._expecting = weakref.WeakValueDictionary()
+        if type(sock) is Channel:
+            # override default logger
+            transport = self.sock.get_transport()
+            self.logger = util.get_logger(
+                transport.get_log_channel() + ".sftp"
+            )
+            self.ultra_debug = transport.get_hexdump()
+        try:
+            server_version = self._send_version()
+        except EOFError:
+            raise SSHException("EOF during negotiation")
+        self._log(
+            INFO,
+            "Opened sftp connection (server version {})".format(
+                server_version
+            ),
+        )
+
+    @classmethod
+    def from_transport(cls, t, window_size=None, max_packet_size=None):
+        """
+        Create an SFTP client channel from an open `.Transport`.
+
+        Setting the window and packet sizes might affect the transfer speed.
+        The default settings in the `.Transport` class are the same as in
+        OpenSSH and should work adequately for both files transfers and
+        interactive sessions.
+
+        :param .Transport t: an open `.Transport` which is already
+            authenticated
+        :param int window_size:
+            optional window size for the `.SFTPClient` session.
+        :param int max_packet_size:
+            optional max packet size for the `.SFTPClient` session..
+
+        :return:
+            a new `.SFTPClient` object, referring to an sftp session (channel)
+            across the transport
+
+        .. versionchanged:: 1.15
+            Added the ``window_size`` and ``max_packet_size`` arguments.
+        """
+        chan = t.open_session(
+            window_size=window_size, max_packet_size=max_packet_size
+        )
+        if chan is None:
+            return None
+        chan.invoke_subsystem("sftp")
+        return cls(chan)
+
+    def _log(self, level, msg, *args):
+        if isinstance(msg, list):
+            for m in msg:
+                self._log(level, m, *args)
+        else:
+            # NOTE: these bits MUST continue using %-style format junk because
+            # logging.Logger.log() explicitly requires it. Grump.
+            # escape '%' in msg (they could come from file or directory names)
+            # before logging
+            msg = msg.replace("%", "%%")
+            super()._log(
+                level,
+                "[chan %s] " + msg,
+                *([self.sock.get_name()] + list(args))
+            )
+
+    def close(self):
+        """
+        Close the SFTP session and its underlying channel.
+
+        .. versionadded:: 1.4
+        """
+        self._log(INFO, "sftp session closed.")
+        self.sock.close()
+
+    def get_channel(self):
+        """
+        Return the underlying `.Channel` object for this SFTP session.  This
+        might be useful for doing things like setting a timeout on the channel.
+
+        .. versionadded:: 1.7.1
+        """
+        return self.sock
+
+    def listdir(self, path="."):
+        """
+        Return a list containing the names of the entries in the given
+        ``path``.
+
+        The list is in arbitrary order.  It does not include the special
+        entries ``'.'`` and ``'..'`` even if they are present in the folder.
+        This method is meant to mirror ``os.listdir`` as closely as possible.
+        For a list of full `.SFTPAttributes` objects, see `listdir_attr`.
+
+        :param str path: path to list (defaults to ``'.'``)
+        """
+        return [f.filename for f in self.listdir_attr(path)]
+
+    def listdir_attr(self, path="."):
+        """
+        Return a list containing `.SFTPAttributes` objects corresponding to
+        files in the given ``path``.  The list is in arbitrary order.  It does
+        not include the special entries ``'.'`` and ``'..'`` even if they are
+        present in the folder.
+
+        The returned `.SFTPAttributes` objects will each have an additional
+        field: ``longname``, which may contain a formatted string of the file's
+        attributes, in unix format.  The content of this string will probably
+        depend on the SFTP server implementation.
+
+        :param str path: path to list (defaults to ``'.'``)
+        :return: list of `.SFTPAttributes` objects
+
+        .. versionadded:: 1.2
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "listdir({!r})".format(path))
+        t, msg = self._request(CMD_OPENDIR, path)
+        if t != CMD_HANDLE:
+            raise SFTPError("Expected handle")
+        handle = msg.get_binary()
+        filelist = []
+        while True:
+            try:
+                t, msg = self._request(CMD_READDIR, handle)
+            except EOFError:
+                # done with handle
+                break
+            if t != CMD_NAME:
+                raise SFTPError("Expected name response")
+            count = msg.get_int()
+            for i in range(count):
+                filename = msg.get_text()
+                longname = msg.get_text()
+                attr = SFTPAttributes._from_msg(msg, filename, longname)
+                if (filename != ".") and (filename != ".."):
+                    filelist.append(attr)
+        self._request(CMD_CLOSE, handle)
+        return filelist
+
+    def listdir_iter(self, path=".", read_aheads=50):
+        """
+        Generator version of `.listdir_attr`.
+
+        See the API docs for `.listdir_attr` for overall details.
+
+        This function adds one more kwarg on top of `.listdir_attr`:
+        ``read_aheads``, an integer controlling how many
+        ``SSH_FXP_READDIR`` requests are made to the server. The default of 50
+        should suffice for most file listings as each request/response cycle
+        may contain multiple files (dependent on server implementation.)
+
+        .. versionadded:: 1.15
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "listdir({!r})".format(path))
+        t, msg = self._request(CMD_OPENDIR, path)
+
+        if t != CMD_HANDLE:
+            raise SFTPError("Expected handle")
+
+        handle = msg.get_string()
+
+        nums = list()
+        while True:
+            try:
+                # Send out a bunch of readdir requests so that we can read the
+                # responses later on Section 6.7 of the SSH file transfer RFC
+                # explains this
+                # http://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
+                for i in range(read_aheads):
+                    num = self._async_request(type(None), CMD_READDIR, handle)
+                    nums.append(num)
+
+                # For each of our sent requests
+                # Read and parse the corresponding packets
+                # If we're at the end of our queued requests, then fire off
+                # some more requests
+                # Exit the loop when we've reached the end of the directory
+                # handle
+                for num in nums:
+                    t, pkt_data = self._read_packet()
+                    msg = Message(pkt_data)
+                    new_num = msg.get_int()
+                    if num == new_num:
+                        if t == CMD_STATUS:
+                            self._convert_status(msg)
+                    count = msg.get_int()
+                    for i in range(count):
+                        filename = msg.get_text()
+                        longname = msg.get_text()
+                        attr = SFTPAttributes._from_msg(
+                            msg, filename, longname
+                        )
+                        if (filename != ".") and (filename != ".."):
+                            yield attr
+
+                # If we've hit the end of our queued requests, reset nums.
+                nums = list()
+
+            except EOFError:
+                self._request(CMD_CLOSE, handle)
+                return
+
+    def open(self, filename, mode="r", bufsize=-1):
+        """
+        Open a file on the remote server.  The arguments are the same as for
+        Python's built-in `python:file` (aka `python:open`).  A file-like
+        object is returned, which closely mimics the behavior of a normal
+        Python file object, including the ability to be used as a context
+        manager.
+
+        The mode indicates how the file is to be opened: ``'r'`` for reading,
+        ``'w'`` for writing (truncating an existing file), ``'a'`` for
+        appending, ``'r+'`` for reading/writing, ``'w+'`` for reading/writing
+        (truncating an existing file), ``'a+'`` for reading/appending.  The
+        Python ``'b'`` flag is ignored, since SSH treats all files as binary.
+        The ``'U'`` flag is supported in a compatible way.
+
+        Since 1.5.2, an ``'x'`` flag indicates that the operation should only
+        succeed if the file was created and did not previously exist.  This has
+        no direct mapping to Python's file flags, but is commonly known as the
+        ``O_EXCL`` flag in posix.
+
+        The file will be buffered in standard Python style by default, but
+        can be altered with the ``bufsize`` parameter.  ``<=0`` turns off
+        buffering, ``1`` uses line buffering, and any number greater than 1
+        (``>1``) uses that specific buffer size.
+
+        :param str filename: name of the file to open
+        :param str mode: mode (Python-style) to open in
+        :param int bufsize: desired buffering (default: ``-1``)
+        :return: an `.SFTPFile` object representing the open file
+
+        :raises: ``IOError`` -- if the file could not be opened.
+        """
+        filename = self._adjust_cwd(filename)
+        self._log(DEBUG, "open({!r}, {!r})".format(filename, mode))
+        imode = 0
+        if ("r" in mode) or ("+" in mode):
+            imode |= SFTP_FLAG_READ
+        if ("w" in mode) or ("+" in mode) or ("a" in mode):
+            imode |= SFTP_FLAG_WRITE
+        if "w" in mode:
+            imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC
+        if "a" in mode:
+            imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPEND
+        if "x" in mode:
+            imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCL
+        attrblock = SFTPAttributes()
+        t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
+        if t != CMD_HANDLE:
+            raise SFTPError("Expected handle")
+        handle = msg.get_binary()
+        self._log(
+            DEBUG,
+            "open({!r}, {!r}) -> {}".format(
+                filename, mode, u(hexlify(handle))
+            ),
+        )
+        return SFTPFile(self, handle, mode, bufsize)
+
+    # Python continues to vacillate about "open" vs "file"...
+    file = open
+
+    def remove(self, path):
+        """
+        Remove the file at the given path.  This only works on files; for
+        removing folders (directories), use `rmdir`.
+
+        :param str path: path (absolute or relative) of the file to remove
+
+        :raises: ``IOError`` -- if the path refers to a folder (directory)
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "remove({!r})".format(path))
+        self._request(CMD_REMOVE, path)
+
+    unlink = remove
+
+    def rename(self, oldpath, newpath):
+        """
+        Rename a file or folder from ``oldpath`` to ``newpath``.
+
+        .. note::
+            This method implements 'standard' SFTP ``RENAME`` behavior; those
+            seeking the OpenSSH "POSIX rename" extension behavior should use
+            `posix_rename`.
+
+        :param str oldpath:
+            existing name of the file or folder
+        :param str newpath:
+            new name for the file or folder, must not exist already
+
+        :raises:
+            ``IOError`` -- if ``newpath`` is a folder, or something else goes
+            wrong
+        """
+        oldpath = self._adjust_cwd(oldpath)
+        newpath = self._adjust_cwd(newpath)
+        self._log(DEBUG, "rename({!r}, {!r})".format(oldpath, newpath))
+        self._request(CMD_RENAME, oldpath, newpath)
+
+    def posix_rename(self, oldpath, newpath):
+        """
+        Rename a file or folder from ``oldpath`` to ``newpath``, following
+        posix conventions.
+
+        :param str oldpath: existing name of the file or folder
+        :param str newpath: new name for the file or folder, will be
+            overwritten if it already exists
+
+        :raises:
+            ``IOError`` -- if ``newpath`` is a folder, posix-rename is not
+            supported by the server or something else goes wrong
+
+        :versionadded: 2.2
+        """
+        oldpath = self._adjust_cwd(oldpath)
+        newpath = self._adjust_cwd(newpath)
+        self._log(DEBUG, "posix_rename({!r}, {!r})".format(oldpath, newpath))
+        self._request(
+            CMD_EXTENDED, "posix-rename@openssh.com", oldpath, newpath
+        )
+
+    def mkdir(self, path, mode=o777):
+        """
+        Create a folder (directory) named ``path`` with numeric mode ``mode``.
+        The default mode is 0777 (octal).  On some systems, mode is ignored.
+        Where it is used, the current umask value is first masked out.
+
+        :param str path: name of the folder to create
+        :param int mode: permissions (posix-style) for the newly-created folder
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "mkdir({!r}, {!r})".format(path, mode))
+        attr = SFTPAttributes()
+        attr.st_mode = mode
+        self._request(CMD_MKDIR, path, attr)
+
+    def rmdir(self, path):
+        """
+        Remove the folder named ``path``.
+
+        :param str path: name of the folder to remove
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "rmdir({!r})".format(path))
+        self._request(CMD_RMDIR, path)
+
+    def stat(self, path):
+        """
+        Retrieve information about a file on the remote system.  The return
+        value is an object whose attributes correspond to the attributes of
+        Python's ``stat`` structure as returned by ``os.stat``, except that it
+        contains fewer fields.  An SFTP server may return as much or as little
+        info as it wants, so the results may vary from server to server.
+
+        Unlike a Python `python:stat` object, the result may not be accessed as
+        a tuple.  This is mostly due to the author's slack factor.
+
+        The fields supported are: ``st_mode``, ``st_size``, ``st_uid``,
+        ``st_gid``, ``st_atime``, and ``st_mtime``.
+
+        :param str path: the filename to stat
+        :return:
+            an `.SFTPAttributes` object containing attributes about the given
+            file
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "stat({!r})".format(path))
+        t, msg = self._request(CMD_STAT, path)
+        if t != CMD_ATTRS:
+            raise SFTPError("Expected attributes")
+        return SFTPAttributes._from_msg(msg)
+
+    def lstat(self, path):
+        """
+        Retrieve information about a file on the remote system, without
+        following symbolic links (shortcuts).  This otherwise behaves exactly
+        the same as `stat`.
+
+        :param str path: the filename to stat
+        :return:
+            an `.SFTPAttributes` object containing attributes about the given
+            file
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "lstat({!r})".format(path))
+        t, msg = self._request(CMD_LSTAT, path)
+        if t != CMD_ATTRS:
+            raise SFTPError("Expected attributes")
+        return SFTPAttributes._from_msg(msg)
+
+    def symlink(self, source, dest):
+        """
+        Create a symbolic link to the ``source`` path at ``destination``.
+
+        :param str source: path of the original file
+        :param str dest: path of the newly created symlink
+        """
+        dest = self._adjust_cwd(dest)
+        self._log(DEBUG, "symlink({!r}, {!r})".format(source, dest))
+        source = b(source)
+        self._request(CMD_SYMLINK, source, dest)
+
+    def chmod(self, path, mode):
+        """
+        Change the mode (permissions) of a file.  The permissions are
+        unix-style and identical to those used by Python's `os.chmod`
+        function.
+
+        :param str path: path of the file to change the permissions of
+        :param int mode: new permissions
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "chmod({!r}, {!r})".format(path, mode))
+        attr = SFTPAttributes()
+        attr.st_mode = mode
+        self._request(CMD_SETSTAT, path, attr)
+
+    def chown(self, path, uid, gid):
+        """
+        Change the owner (``uid``) and group (``gid``) of a file.  As with
+        Python's `os.chown` function, you must pass both arguments, so if you
+        only want to change one, use `stat` first to retrieve the current
+        owner and group.
+
+        :param str path: path of the file to change the owner and group of
+        :param int uid: new owner's uid
+        :param int gid: new group id
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "chown({!r}, {!r}, {!r})".format(path, uid, gid))
+        attr = SFTPAttributes()
+        attr.st_uid, attr.st_gid = uid, gid
+        self._request(CMD_SETSTAT, path, attr)
+
+    def utime(self, path, times):
+        """
+        Set the access and modified times of the file specified by ``path``.
+        If ``times`` is ``None``, then the file's access and modified times
+        are set to the current time.  Otherwise, ``times`` must be a 2-tuple
+        of numbers, of the form ``(atime, mtime)``, which is used to set the
+        access and modified times, respectively.  This bizarre API is mimicked
+        from Python for the sake of consistency -- I apologize.
+
+        :param str path: path of the file to modify
+        :param tuple times:
+            ``None`` or a tuple of (access time, modified time) in standard
+            internet epoch time (seconds since 01 January 1970 GMT)
+        """
+        path = self._adjust_cwd(path)
+        if times is None:
+            times = (time.time(), time.time())
+        self._log(DEBUG, "utime({!r}, {!r})".format(path, times))
+        attr = SFTPAttributes()
+        attr.st_atime, attr.st_mtime = times
+        self._request(CMD_SETSTAT, path, attr)
+
+    def truncate(self, path, size):
+        """
+        Change the size of the file specified by ``path``.  This usually
+        extends or shrinks the size of the file, just like the `~file.truncate`
+        method on Python file objects.
+
+        :param str path: path of the file to modify
+        :param int size: the new size of the file
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "truncate({!r}, {!r})".format(path, size))
+        attr = SFTPAttributes()
+        attr.st_size = size
+        self._request(CMD_SETSTAT, path, attr)
+
+    def readlink(self, path):
+        """
+        Return the target of a symbolic link (shortcut).  You can use
+        `symlink` to create these.  The result may be either an absolute or
+        relative pathname.
+
+        :param str path: path of the symbolic link file
+        :return: target path, as a `str`
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "readlink({!r})".format(path))
+        t, msg = self._request(CMD_READLINK, path)
+        if t != CMD_NAME:
+            raise SFTPError("Expected name response")
+        count = msg.get_int()
+        if count == 0:
+            return None
+        if count != 1:
+            raise SFTPError("Readlink returned {} results".format(count))
+        return _to_unicode(msg.get_string())
+
+    def normalize(self, path):
+        """
+        Return the normalized path (on the server) of a given path.  This
+        can be used to quickly resolve symbolic links or determine what the
+        server is considering to be the "current folder" (by passing ``'.'``
+        as ``path``).
+
+        :param str path: path to be normalized
+        :return: normalized form of the given path (as a `str`)
+
+        :raises: ``IOError`` -- if the path can't be resolved on the server
+        """
+        path = self._adjust_cwd(path)
+        self._log(DEBUG, "normalize({!r})".format(path))
+        t, msg = self._request(CMD_REALPATH, path)
+        if t != CMD_NAME:
+            raise SFTPError("Expected name response")
+        count = msg.get_int()
+        if count != 1:
+            raise SFTPError("Realpath returned {} results".format(count))
+        return msg.get_text()
+
+    def chdir(self, path=None):
+        """
+        Change the "current directory" of this SFTP session.  Since SFTP
+        doesn't really have the concept of a current working directory, this is
+        emulated by Paramiko.  Once you use this method to set a working
+        directory, all operations on this `.SFTPClient` object will be relative
+        to that path. You can pass in ``None`` to stop using a current working
+        directory.
+
+        :param str path: new current working directory
+
+        :raises:
+            ``IOError`` -- if the requested path doesn't exist on the server
+
+        .. versionadded:: 1.4
+        """
+        if path is None:
+            self._cwd = None
+            return
+        if not stat.S_ISDIR(self.stat(path).st_mode):
+            code = errno.ENOTDIR
+            raise SFTPError(code, "{}: {}".format(os.strerror(code), path))
+        self._cwd = b(self.normalize(path))
+
+    def getcwd(self):
+        """
+        Return the "current working directory" for this SFTP session, as
+        emulated by Paramiko.  If no directory has been set with `chdir`,
+        this method will return ``None``.
+
+        .. versionadded:: 1.4
+        """
+        # TODO: make class initialize with self._cwd set to self.normalize('.')
+        return self._cwd and u(self._cwd)
+
+    def _transfer_with_callback(self, reader, writer, file_size, callback):
+        size = 0
+        while True:
+            data = reader.read(32768)
+            writer.write(data)
+            size += len(data)
+            if len(data) == 0:
+                break
+            if callback is not None:
+                callback(size, file_size)
+        return size
+
+    def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
+        """
+        Copy the contents of an open file object (``fl``) to the SFTP server as
+        ``remotepath``. Any exception raised by operations will be passed
+        through.
+
+        The SFTP operations use pipelining for speed.
+
+        :param fl: opened file or file-like object to copy
+        :param str remotepath: the destination path on the SFTP server
+        :param int file_size:
+            optional size parameter passed to callback. If none is specified,
+            size defaults to 0
+        :param callable callback:
+            optional callback function (form: ``func(int, int)``) that accepts
+            the bytes transferred so far and the total bytes to be transferred
+            (since 1.7.4)
+        :param bool confirm:
+            whether to do a stat() on the file afterwards to confirm the file
+            size (since 1.7.7)
+
+        :return:
+            an `.SFTPAttributes` object containing attributes about the given
+            file.
+
+        .. versionadded:: 1.10
+        """
+        with self.file(remotepath, "wb") as fr:
+            fr.set_pipelined(True)
+            size = self._transfer_with_callback(
+                reader=fl, writer=fr, file_size=file_size, callback=callback
+            )
+        if confirm:
+            s = self.stat(remotepath)
+            if s.st_size != size:
+                raise IOError(
+                    "size mismatch in put!  {} != {}".format(s.st_size, size)
+                )
+        else:
+            s = SFTPAttributes()
+        return s
+
+    def put(self, localpath, remotepath, callback=None, confirm=True):
+        """
+        Copy a local file (``localpath``) to the SFTP server as ``remotepath``.
+        Any exception raised by operations will be passed through.  This
+        method is primarily provided as a convenience.
+
+        The SFTP operations use pipelining for speed.
+
+        :param str localpath: the local file to copy
+        :param str remotepath: the destination path on the SFTP server. Note
+            that the filename should be included. Only specifying a directory
+            may result in an error.
+        :param callable callback:
+            optional callback function (form: ``func(int, int)``) that accepts
+            the bytes transferred so far and the total bytes to be transferred
+        :param bool confirm:
+            whether to do a stat() on the file afterwards to confirm the file
+            size
+
+        :return: an `.SFTPAttributes` object containing attributes about the
+            given file
+
+        .. versionadded:: 1.4
+        .. versionchanged:: 1.7.4
+            ``callback`` and rich attribute return value added.
+        .. versionchanged:: 1.7.7
+            ``confirm`` param added.
+        """
+        file_size = os.stat(localpath).st_size
+        with open(localpath, "rb") as fl:
+            return self.putfo(fl, remotepath, file_size, callback, confirm)
+
+    def getfo(self, remotepath, fl, callback=None, prefetch=True):
+        """
+        Copy a remote file (``remotepath``) from the SFTP server and write to
+        an open file or file-like object, ``fl``.  Any exception raised by
+        operations will be passed through.  This method is primarily provided
+        as a convenience.
+
+        :param object remotepath: opened file or file-like object to copy to
+        :param str fl:
+            the destination path on the local host or open file object
+        :param callable callback:
+            optional callback function (form: ``func(int, int)``) that accepts
+            the bytes transferred so far and the total bytes to be transferred
+        :param bool prefetch:
+            controls whether prefetching is performed (default: True)
+        :return: the `number <int>` of bytes written to the opened file object
+
+        .. versionadded:: 1.10
+        .. versionchanged:: 2.8
+            Added the ``prefetch`` keyword argument.
+        """
+        file_size = self.stat(remotepath).st_size
+        with self.open(remotepath, "rb") as fr:
+            if prefetch:
+                fr.prefetch(file_size)
+            return self._transfer_with_callback(
+                reader=fr, writer=fl, file_size=file_size, callback=callback
+            )
+
+    def get(self, remotepath, localpath, callback=None, prefetch=True):
+        """
+        Copy a remote file (``remotepath``) from the SFTP server to the local
+        host as ``localpath``.  Any exception raised by operations will be
+        passed through.  This method is primarily provided as a convenience.
+
+        :param str remotepath: the remote file to copy
+        :param str localpath: the destination path on the local host
+        :param callable callback:
+            optional callback function (form: ``func(int, int)``) that accepts
+            the bytes transferred so far and the total bytes to be transferred
+        :param bool prefetch:
+            controls whether prefetching is performed (default: True)
+
+        .. versionadded:: 1.4
+        .. versionchanged:: 1.7.4
+            Added the ``callback`` param
+        .. versionchanged:: 2.8
+            Added the ``prefetch`` keyword argument.
+        """
+        with open(localpath, "wb") as fl:
+            size = self.getfo(remotepath, fl, callback, prefetch)
+        s = os.stat(localpath)
+        if s.st_size != size:
+            raise IOError(
+                "size mismatch in get!  {} != {}".format(s.st_size, size)
+            )
+
+    # ...internals...
+
+    def _request(self, t, *args):
+        num = self._async_request(type(None), t, *args)
+        return self._read_response(num)
+
+    def _async_request(self, fileobj, t, *args):
+        # this method may be called from other threads (prefetch)
+        self._lock.acquire()
+        try:
+            msg = Message()
+            msg.add_int(self.request_number)
+            for item in args:
+                if isinstance(item, int64):
+                    msg.add_int64(item)
+                elif isinstance(item, int):
+                    msg.add_int(item)
+                elif isinstance(item, SFTPAttributes):
+                    item._pack(msg)
+                else:
+                    # For all other types, rely on as_string() to either coerce
+                    # to bytes before writing or raise a suitable exception.
+                    msg.add_string(item)
+            num = self.request_number
+            self._expecting[num] = fileobj
+            self.request_number += 1
+        finally:
+            self._lock.release()
+        self._send_packet(t, msg)
+        return num
+
+    def _read_response(self, waitfor=None):
+        while True:
+            try:
+                t, data = self._read_packet()
+            except EOFError as e:
+                raise SSHException("Server connection dropped: {}".format(e))
+            msg = Message(data)
+            num = msg.get_int()
+            self._lock.acquire()
+            try:
+                if num not in self._expecting:
+                    # might be response for a file that was closed before
+                    # responses came back
+                    self._log(DEBUG, "Unexpected response #{}".format(num))
+                    if waitfor is None:
+                        # just doing a single check
+                        break
+                    continue
+                fileobj = self._expecting[num]
+                del self._expecting[num]
+            finally:
+                self._lock.release()
+            if num == waitfor:
+                # synchronous
+                if t == CMD_STATUS:
+                    self._convert_status(msg)
+                return t, msg
+
+            # can not rewrite this to deal with E721, either as a None check
+            # nor as not an instance of None or NoneType
+            if fileobj is not type(None):  # noqa
+                fileobj._async_response(t, msg, num)
+            if waitfor is None:
+                # just doing a single check
+                break
+        return None, None
+
+    def _finish_responses(self, fileobj):
+        while fileobj in self._expecting.values():
+            self._read_response()
+            fileobj._check_exception()
+
+    def _convert_status(self, msg):
+        """
+        Raises EOFError or IOError on error status; otherwise does nothing.
+        """
+        code = msg.get_int()
+        text = msg.get_text()
+        if code == SFTP_OK:
+            return
+        elif code == SFTP_EOF:
+            raise EOFError(text)
+        elif code == SFTP_NO_SUCH_FILE:
+            # clever idea from john a. meinel: map the error codes to errno
+            raise IOError(errno.ENOENT, text)
+        elif code == SFTP_PERMISSION_DENIED:
+            raise IOError(errno.EACCES, text)
+        else:
+            raise IOError(text)
+
+    def _adjust_cwd(self, path):
+        """
+        Return an adjusted path if we're emulating a "current working
+        directory" for the server.
+        """
+        path = b(path)
+        if self._cwd is None:
+            return path
+        if len(path) and path[0:1] == b_slash:
+            # absolute path
+            return path
+        if self._cwd == b_slash:
+            return self._cwd + path
+        return self._cwd + b_slash + path
+
+
+class SFTP(SFTPClient):
+    """
+    An alias for `.SFTPClient` for backwards compatibility.
+    """
+
+    pass
diff --git a/paramiko/sftp_file.py b/paramiko/sftp_file.py
new file mode 100644
index 0000000..9a0a6b3
--- /dev/null
+++ b/paramiko/sftp_file.py
@@ -0,0 +1,570 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+SFTP file object
+"""
+
+
+from binascii import hexlify
+from collections import deque
+import socket
+import threading
+import time
+from paramiko.common import DEBUG
+
+from paramiko.file import BufferedFile
+from paramiko.util import u
+from paramiko.sftp import (
+    CMD_CLOSE,
+    CMD_READ,
+    CMD_DATA,
+    SFTPError,
+    CMD_WRITE,
+    CMD_STATUS,
+    CMD_FSTAT,
+    CMD_ATTRS,
+    CMD_FSETSTAT,
+    CMD_EXTENDED,
+    int64,
+)
+from paramiko.sftp_attr import SFTPAttributes
+
+
+class SFTPFile(BufferedFile):
+    """
+    Proxy object for a file on the remote server, in client mode SFTP.
+
+    Instances of this class may be used as context managers in the same way
+    that built-in Python file objects are.
+    """
+
+    # Some sftp servers will choke if you send read/write requests larger than
+    # this size.
+    MAX_REQUEST_SIZE = 32768
+
+    def __init__(self, sftp, handle, mode="r", bufsize=-1):
+        BufferedFile.__init__(self)
+        self.sftp = sftp
+        self.handle = handle
+        BufferedFile._set_mode(self, mode, bufsize)
+        self.pipelined = False
+        self._prefetching = False
+        self._prefetch_done = False
+        self._prefetch_data = {}
+        self._prefetch_extents = {}
+        self._prefetch_lock = threading.Lock()
+        self._saved_exception = None
+        self._reqs = deque()
+
+    def __del__(self):
+        self._close(async_=True)
+
+    def close(self):
+        """
+        Close the file.
+        """
+        self._close(async_=False)
+
+    def _close(self, async_=False):
+        # We allow double-close without signaling an error, because real
+        # Python file objects do.  However, we must protect against actually
+        # sending multiple CMD_CLOSE packets, because after we close our
+        # handle, the same handle may be re-allocated by the server, and we
+        # may end up mysteriously closing some random other file.  (This is
+        # especially important because we unconditionally call close() from
+        # __del__.)
+        if self._closed:
+            return
+        self.sftp._log(DEBUG, "close({})".format(u(hexlify(self.handle))))
+        if self.pipelined:
+            self.sftp._finish_responses(self)
+        BufferedFile.close(self)
+        try:
+            if async_:
+                # GC'd file handle could be called from an arbitrary thread
+                # -- don't wait for a response
+                self.sftp._async_request(type(None), CMD_CLOSE, self.handle)
+            else:
+                self.sftp._request(CMD_CLOSE, self.handle)
+        except EOFError:
+            # may have outlived the Transport connection
+            pass
+        except (IOError, socket.error):
+            # may have outlived the Transport connection
+            pass
+
+    def _data_in_prefetch_requests(self, offset, size):
+        k = [
+            x for x in list(self._prefetch_extents.values()) if x[0] <= offset
+        ]
+        if len(k) == 0:
+            return False
+        k.sort(key=lambda x: x[0])
+        buf_offset, buf_size = k[-1]
+        if buf_offset + buf_size <= offset:
+            # prefetch request ends before this one begins
+            return False
+        if buf_offset + buf_size >= offset + size:
+            # inclusive
+            return True
+        # well, we have part of the request.  see if another chunk has
+        # the rest.
+        return self._data_in_prefetch_requests(
+            buf_offset + buf_size, offset + size - buf_offset - buf_size
+        )
+
+    def _data_in_prefetch_buffers(self, offset):
+        """
+        if a block of data is present in the prefetch buffers, at the given
+        offset, return the offset of the relevant prefetch buffer.  otherwise,
+        return None.  this guarantees nothing about the number of bytes
+        collected in the prefetch buffer so far.
+        """
+        k = [i for i in self._prefetch_data.keys() if i <= offset]
+        if len(k) == 0:
+            return None
+        index = max(k)
+        buf_offset = offset - index
+        if buf_offset >= len(self._prefetch_data[index]):
+            # it's not here
+            return None
+        return index
+
+    def _read_prefetch(self, size):
+        """
+        read data out of the prefetch buffer, if possible.  if the data isn't
+        in the buffer, return None.  otherwise, behaves like a normal read.
+        """
+        # while not closed, and haven't fetched past the current position,
+        # and haven't reached EOF...
+        while True:
+            offset = self._data_in_prefetch_buffers(self._realpos)
+            if offset is not None:
+                break
+            if self._prefetch_done or self._closed:
+                break
+            self.sftp._read_response()
+            self._check_exception()
+        if offset is None:
+            self._prefetching = False
+            return None
+        prefetch = self._prefetch_data[offset]
+        del self._prefetch_data[offset]
+
+        buf_offset = self._realpos - offset
+        if buf_offset > 0:
+            self._prefetch_data[offset] = prefetch[:buf_offset]
+            prefetch = prefetch[buf_offset:]
+        if size < len(prefetch):
+            self._prefetch_data[self._realpos + size] = prefetch[size:]
+            prefetch = prefetch[:size]
+        return prefetch
+
+    def _read(self, size):
+        size = min(size, self.MAX_REQUEST_SIZE)
+        if self._prefetching:
+            data = self._read_prefetch(size)
+            if data is not None:
+                return data
+        t, msg = self.sftp._request(
+            CMD_READ, self.handle, int64(self._realpos), int(size)
+        )
+        if t != CMD_DATA:
+            raise SFTPError("Expected data")
+        return msg.get_string()
+
+    def _write(self, data):
+        # may write less than requested if it would exceed max packet size
+        chunk = min(len(data), self.MAX_REQUEST_SIZE)
+        sftp_async_request = self.sftp._async_request(
+            type(None),
+            CMD_WRITE,
+            self.handle,
+            int64(self._realpos),
+            data[:chunk],
+        )
+        self._reqs.append(sftp_async_request)
+        if not self.pipelined or (
+            len(self._reqs) > 100 and self.sftp.sock.recv_ready()
+        ):
+            while len(self._reqs):
+                req = self._reqs.popleft()
+                t, msg = self.sftp._read_response(req)
+                if t != CMD_STATUS:
+                    raise SFTPError("Expected status")
+                # convert_status already called
+        return chunk
+
+    def settimeout(self, timeout):
+        """
+        Set a timeout on read/write operations on the underlying socket or
+        ssh `.Channel`.
+
+        :param float timeout:
+            seconds to wait for a pending read/write operation before raising
+            ``socket.timeout``, or ``None`` for no timeout
+
+        .. seealso:: `.Channel.settimeout`
+        """
+        self.sftp.sock.settimeout(timeout)
+
+    def gettimeout(self):
+        """
+        Returns the timeout in seconds (as a `float`) associated with the
+        socket or ssh `.Channel` used for this file.
+
+        .. seealso:: `.Channel.gettimeout`
+        """
+        return self.sftp.sock.gettimeout()
+
+    def setblocking(self, blocking):
+        """
+        Set blocking or non-blocking mode on the underiying socket or ssh
+        `.Channel`.
+
+        :param int blocking:
+            0 to set non-blocking mode; non-0 to set blocking mode.
+
+        .. seealso:: `.Channel.setblocking`
+        """
+        self.sftp.sock.setblocking(blocking)
+
+    def seekable(self):
+        """
+        Check if the file supports random access.
+
+        :return:
+            `True` if the file supports random access. If `False`,
+            :meth:`seek` will raise an exception
+        """
+        return True
+
+    def seek(self, offset, whence=0):
+        """
+        Set the file's current position.
+
+        See `file.seek` for details.
+        """
+        self.flush()
+        if whence == self.SEEK_SET:
+            self._realpos = self._pos = offset
+        elif whence == self.SEEK_CUR:
+            self._pos += offset
+            self._realpos = self._pos
+        else:
+            self._realpos = self._pos = self._get_size() + offset
+        self._rbuffer = bytes()
+
+    def stat(self):
+        """
+        Retrieve information about this file from the remote system.  This is
+        exactly like `.SFTPClient.stat`, except that it operates on an
+        already-open file.
+
+        :returns:
+            an `.SFTPAttributes` object containing attributes about this file.
+        """
+        t, msg = self.sftp._request(CMD_FSTAT, self.handle)
+        if t != CMD_ATTRS:
+            raise SFTPError("Expected attributes")
+        return SFTPAttributes._from_msg(msg)
+
+    def chmod(self, mode):
+        """
+        Change the mode (permissions) of this file.  The permissions are
+        unix-style and identical to those used by Python's `os.chmod`
+        function.
+
+        :param int mode: new permissions
+        """
+        self.sftp._log(
+            DEBUG, "chmod({}, {!r})".format(hexlify(self.handle), mode)
+        )
+        attr = SFTPAttributes()
+        attr.st_mode = mode
+        self.sftp._request(CMD_FSETSTAT, self.handle, attr)
+
+    def chown(self, uid, gid):
+        """
+        Change the owner (``uid``) and group (``gid``) of this file.  As with
+        Python's `os.chown` function, you must pass both arguments, so if you
+        only want to change one, use `stat` first to retrieve the current
+        owner and group.
+
+        :param int uid: new owner's uid
+        :param int gid: new group id
+        """
+        self.sftp._log(
+            DEBUG,
+            "chown({}, {!r}, {!r})".format(hexlify(self.handle), uid, gid),
+        )
+        attr = SFTPAttributes()
+        attr.st_uid, attr.st_gid = uid, gid
+        self.sftp._request(CMD_FSETSTAT, self.handle, attr)
+
+    def utime(self, times):
+        """
+        Set the access and modified times of this file.  If
+        ``times`` is ``None``, then the file's access and modified times are
+        set to the current time.  Otherwise, ``times`` must be a 2-tuple of
+        numbers, of the form ``(atime, mtime)``, which is used to set the
+        access and modified times, respectively.  This bizarre API is mimicked
+        from Python for the sake of consistency -- I apologize.
+
+        :param tuple times:
+            ``None`` or a tuple of (access time, modified time) in standard
+            internet epoch time (seconds since 01 January 1970 GMT)
+        """
+        if times is None:
+            times = (time.time(), time.time())
+        self.sftp._log(
+            DEBUG, "utime({}, {!r})".format(hexlify(self.handle), times)
+        )
+        attr = SFTPAttributes()
+        attr.st_atime, attr.st_mtime = times
+        self.sftp._request(CMD_FSETSTAT, self.handle, attr)
+
+    def truncate(self, size):
+        """
+        Change the size of this file.  This usually extends
+        or shrinks the size of the file, just like the ``truncate()`` method on
+        Python file objects.
+
+        :param size: the new size of the file
+        """
+        self.sftp._log(
+            DEBUG, "truncate({}, {!r})".format(hexlify(self.handle), size)
+        )
+        attr = SFTPAttributes()
+        attr.st_size = size
+        self.sftp._request(CMD_FSETSTAT, self.handle, attr)
+
+    def check(self, hash_algorithm, offset=0, length=0, block_size=0):
+        """
+        Ask the server for a hash of a section of this file.  This can be used
+        to verify a successful upload or download, or for various rsync-like
+        operations.
+
+        The file is hashed from ``offset``, for ``length`` bytes.
+        If ``length`` is 0, the remainder of the file is hashed.  Thus, if both
+        ``offset`` and ``length`` are zero, the entire file is hashed.
+
+        Normally, ``block_size`` will be 0 (the default), and this method will
+        return a byte string representing the requested hash (for example, a
+        string of length 16 for MD5, or 20 for SHA-1).  If a non-zero
+        ``block_size`` is given, each chunk of the file (from ``offset`` to
+        ``offset + length``) of ``block_size`` bytes is computed as a separate
+        hash.  The hash results are all concatenated and returned as a single
+        string.
+
+        For example, ``check('sha1', 0, 1024, 512)`` will return a string of
+        length 40.  The first 20 bytes will be the SHA-1 of the first 512 bytes
+        of the file, and the last 20 bytes will be the SHA-1 of the next 512
+        bytes.
+
+        :param str hash_algorithm:
+            the name of the hash algorithm to use (normally ``"sha1"`` or
+            ``"md5"``)
+        :param offset:
+            offset into the file to begin hashing (0 means to start from the
+            beginning)
+        :param length:
+            number of bytes to hash (0 means continue to the end of the file)
+        :param int block_size:
+            number of bytes to hash per result (must not be less than 256; 0
+            means to compute only one hash of the entire segment)
+        :return:
+            `str` of bytes representing the hash of each block, concatenated
+            together
+
+        :raises:
+            ``IOError`` -- if the server doesn't support the "check-file"
+            extension, or possibly doesn't support the hash algorithm requested
+
+        .. note:: Many (most?) servers don't support this extension yet.
+
+        .. versionadded:: 1.4
+        """
+        t, msg = self.sftp._request(
+            CMD_EXTENDED,
+            "check-file",
+            self.handle,
+            hash_algorithm,
+            int64(offset),
+            int64(length),
+            block_size,
+        )
+        msg.get_text()  # ext
+        msg.get_text()  # alg
+        data = msg.get_remainder()
+        return data
+
+    def set_pipelined(self, pipelined=True):
+        """
+        Turn on/off the pipelining of write operations to this file.  When
+        pipelining is on, paramiko won't wait for the server response after
+        each write operation.  Instead, they're collected as they come in. At
+        the first non-write operation (including `.close`), all remaining
+        server responses are collected.  This means that if there was an error
+        with one of your later writes, an exception might be thrown from within
+        `.close` instead of `.write`.
+
+        By default, files are not pipelined.
+
+        :param bool pipelined:
+            ``True`` if pipelining should be turned on for this file; ``False``
+            otherwise
+
+        .. versionadded:: 1.5
+        """
+        self.pipelined = pipelined
+
+    def prefetch(self, file_size=None):
+        """
+        Pre-fetch the remaining contents of this file in anticipation of future
+        `.read` calls.  If reading the entire file, pre-fetching can
+        dramatically improve the download speed by avoiding roundtrip latency.
+        The file's contents are incrementally buffered in a background thread.
+
+        The prefetched data is stored in a buffer until read via the `.read`
+        method.  Once data has been read, it's removed from the buffer.  The
+        data may be read in a random order (using `.seek`); chunks of the
+        buffer that haven't been read will continue to be buffered.
+
+        :param int file_size:
+            When this is ``None`` (the default), this method calls `stat` to
+            determine the remote file size. In some situations, doing so can
+            cause exceptions or hangs (see `#562
+            <https://github.com/paramiko/paramiko/pull/562>`_); as a
+            workaround, one may call `stat` explicitly and pass its value in
+            via this parameter.
+
+        .. versionadded:: 1.5.1
+        .. versionchanged:: 1.16.0
+            The ``file_size`` parameter was added (with no default value).
+        .. versionchanged:: 1.16.1
+            The ``file_size`` parameter was made optional for backwards
+            compatibility.
+        """
+        if file_size is None:
+            file_size = self.stat().st_size
+
+        # queue up async reads for the rest of the file
+        chunks = []
+        n = self._realpos
+        while n < file_size:
+            chunk = min(self.MAX_REQUEST_SIZE, file_size - n)
+            chunks.append((n, chunk))
+            n += chunk
+        if len(chunks) > 0:
+            self._start_prefetch(chunks)
+
+    def readv(self, chunks):
+        """
+        Read a set of blocks from the file by (offset, length).  This is more
+        efficient than doing a series of `.seek` and `.read` calls, since the
+        prefetch machinery is used to retrieve all the requested blocks at
+        once.
+
+        :param chunks:
+            a list of ``(offset, length)`` tuples indicating which sections of
+            the file to read
+        :return: a list of blocks read, in the same order as in ``chunks``
+
+        .. versionadded:: 1.5.4
+        """
+        self.sftp._log(
+            DEBUG, "readv({}, {!r})".format(hexlify(self.handle), chunks)
+        )
+
+        read_chunks = []
+        for offset, size in chunks:
+            # don't fetch data that's already in the prefetch buffer
+            if self._data_in_prefetch_buffers(
+                offset
+            ) or self._data_in_prefetch_requests(offset, size):
+                continue
+
+            # break up anything larger than the max read size
+            while size > 0:
+                chunk_size = min(size, self.MAX_REQUEST_SIZE)
+                read_chunks.append((offset, chunk_size))
+                offset += chunk_size
+                size -= chunk_size
+
+        self._start_prefetch(read_chunks)
+        # now we can just devolve to a bunch of read()s :)
+        for x in chunks:
+            self.seek(x[0])
+            yield self.read(x[1])
+
+    # ...internals...
+
+    def _get_size(self):
+        try:
+            return self.stat().st_size
+        except:
+            return 0
+
+    def _start_prefetch(self, chunks):
+        self._prefetching = True
+        self._prefetch_done = False
+
+        t = threading.Thread(target=self._prefetch_thread, args=(chunks,))
+        t.daemon = True
+        t.start()
+
+    def _prefetch_thread(self, chunks):
+        # do these read requests in a temporary thread because there may be
+        # a lot of them, so it may block.
+        for offset, length in chunks:
+            num = self.sftp._async_request(
+                self, CMD_READ, self.handle, int64(offset), int(length)
+            )
+            with self._prefetch_lock:
+                self._prefetch_extents[num] = (offset, length)
+
+    def _async_response(self, t, msg, num):
+        if t == CMD_STATUS:
+            # save exception and re-raise it on next file operation
+            try:
+                self.sftp._convert_status(msg)
+            except Exception as e:
+                self._saved_exception = e
+            return
+        if t != CMD_DATA:
+            raise SFTPError("Expected data")
+        data = msg.get_string()
+        while True:
+            with self._prefetch_lock:
+                # spin if in race with _prefetch_thread
+                if num in self._prefetch_extents:
+                    offset, length = self._prefetch_extents[num]
+                    self._prefetch_data[offset] = data
+                    del self._prefetch_extents[num]
+                    if len(self._prefetch_extents) == 0:
+                        self._prefetch_done = True
+                    break
+
+    def _check_exception(self):
+        """if there's a saved exception, raise & clear it"""
+        if self._saved_exception is not None:
+            x = self._saved_exception
+            self._saved_exception = None
+            raise x
diff --git a/paramiko/sftp_handle.py b/paramiko/sftp_handle.py
new file mode 100644
index 0000000..b204652
--- /dev/null
+++ b/paramiko/sftp_handle.py
@@ -0,0 +1,196 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Abstraction of an SFTP file handle (for server mode).
+"""
+
+import os
+from paramiko.sftp import SFTP_OP_UNSUPPORTED, SFTP_OK
+from paramiko.util import ClosingContextManager
+
+
+class SFTPHandle(ClosingContextManager):
+    """
+    Abstract object representing a handle to an open file (or folder) in an
+    SFTP server implementation.  Each handle has a string representation used
+    by the client to refer to the underlying file.
+
+    Server implementations can (and should) subclass SFTPHandle to implement
+    features of a file handle, like `stat` or `chattr`.
+
+    Instances of this class may be used as context managers.
+    """
+
+    def __init__(self, flags=0):
+        """
+        Create a new file handle representing a local file being served over
+        SFTP.  If ``flags`` is passed in, it's used to determine if the file
+        is open in append mode.
+
+        :param int flags: optional flags as passed to
+            `.SFTPServerInterface.open`
+        """
+        self.__flags = flags
+        self.__name = None
+        # only for handles to folders:
+        self.__files = {}
+        self.__tell = None
+
+    def close(self):
+        """
+        When a client closes a file, this method is called on the handle.
+        Normally you would use this method to close the underlying OS level
+        file object(s).
+
+        The default implementation checks for attributes on ``self`` named
+        ``readfile`` and/or ``writefile``, and if either or both are present,
+        their ``close()`` methods are called.  This means that if you are
+        using the default implementations of `read` and `write`, this
+        method's default implementation should be fine also.
+        """
+        readfile = getattr(self, "readfile", None)
+        if readfile is not None:
+            readfile.close()
+        writefile = getattr(self, "writefile", None)
+        if writefile is not None:
+            writefile.close()
+
+    def read(self, offset, length):
+        """
+        Read up to ``length`` bytes from this file, starting at position
+        ``offset``.  The offset may be a Python long, since SFTP allows it
+        to be 64 bits.
+
+        If the end of the file has been reached, this method may return an
+        empty string to signify EOF, or it may also return ``SFTP_EOF``.
+
+        The default implementation checks for an attribute on ``self`` named
+        ``readfile``, and if present, performs the read operation on the Python
+        file-like object found there.  (This is meant as a time saver for the
+        common case where you are wrapping a Python file object.)
+
+        :param offset: position in the file to start reading from.
+        :param int length: number of bytes to attempt to read.
+        :return: the `bytes` read, or an error code `int`.
+        """
+        readfile = getattr(self, "readfile", None)
+        if readfile is None:
+            return SFTP_OP_UNSUPPORTED
+        try:
+            if self.__tell is None:
+                self.__tell = readfile.tell()
+            if offset != self.__tell:
+                readfile.seek(offset)
+                self.__tell = offset
+            data = readfile.read(length)
+        except IOError as e:
+            self.__tell = None
+            return SFTPServer.convert_errno(e.errno)
+        self.__tell += len(data)
+        return data
+
+    def write(self, offset, data):
+        """
+        Write ``data`` into this file at position ``offset``.  Extending the
+        file past its original end is expected.  Unlike Python's normal
+        ``write()`` methods, this method cannot do a partial write: it must
+        write all of ``data`` or else return an error.
+
+        The default implementation checks for an attribute on ``self`` named
+        ``writefile``, and if present, performs the write operation on the
+        Python file-like object found there.  The attribute is named
+        differently from ``readfile`` to make it easy to implement read-only
+        (or write-only) files, but if both attributes are present, they should
+        refer to the same file.
+
+        :param offset: position in the file to start reading from.
+        :param bytes data: data to write into the file.
+        :return: an SFTP error code like ``SFTP_OK``.
+        """
+        writefile = getattr(self, "writefile", None)
+        if writefile is None:
+            return SFTP_OP_UNSUPPORTED
+        try:
+            # in append mode, don't care about seeking
+            if (self.__flags & os.O_APPEND) == 0:
+                if self.__tell is None:
+                    self.__tell = writefile.tell()
+                if offset != self.__tell:
+                    writefile.seek(offset)
+                    self.__tell = offset
+            writefile.write(data)
+            writefile.flush()
+        except IOError as e:
+            self.__tell = None
+            return SFTPServer.convert_errno(e.errno)
+        if self.__tell is not None:
+            self.__tell += len(data)
+        return SFTP_OK
+
+    def stat(self):
+        """
+        Return an `.SFTPAttributes` object referring to this open file, or an
+        error code.  This is equivalent to `.SFTPServerInterface.stat`, except
+        it's called on an open file instead of a path.
+
+        :return:
+            an attributes object for the given file, or an SFTP error code
+            (like ``SFTP_PERMISSION_DENIED``).
+        :rtype: `.SFTPAttributes` or error code
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def chattr(self, attr):
+        """
+        Change the attributes of this file.  The ``attr`` object will contain
+        only those fields provided by the client in its request, so you should
+        check for the presence of fields before using them.
+
+        :param .SFTPAttributes attr: the attributes to change on this file.
+        :return: an `int` error code like ``SFTP_OK``.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    # ...internals...
+
+    def _set_files(self, files):
+        """
+        Used by the SFTP server code to cache a directory listing.  (In
+        the SFTP protocol, listing a directory is a multi-stage process
+        requiring a temporary handle.)
+        """
+        self.__files = files
+
+    def _get_next_files(self):
+        """
+        Used by the SFTP server code to retrieve a cached directory
+        listing.
+        """
+        fnlist = self.__files[:16]
+        self.__files = self.__files[16:]
+        return fnlist
+
+    def _get_name(self):
+        return self.__name
+
+    def _set_name(self, name):
+        self.__name = name
+
+
+from paramiko.sftp_server import SFTPServer
diff --git a/paramiko/sftp_server.py b/paramiko/sftp_server.py
new file mode 100644
index 0000000..cd3910d
--- /dev/null
+++ b/paramiko/sftp_server.py
@@ -0,0 +1,537 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Server-mode SFTP support.
+"""
+
+import os
+import errno
+import sys
+from hashlib import md5, sha1
+
+from paramiko import util
+from paramiko.sftp import (
+    BaseSFTP,
+    Message,
+    SFTP_FAILURE,
+    SFTP_PERMISSION_DENIED,
+    SFTP_NO_SUCH_FILE,
+    int64,
+)
+from paramiko.sftp_si import SFTPServerInterface
+from paramiko.sftp_attr import SFTPAttributes
+from paramiko.common import DEBUG
+from paramiko.server import SubsystemHandler
+from paramiko.util import b
+
+
+# known hash algorithms for the "check-file" extension
+from paramiko.sftp import (
+    CMD_HANDLE,
+    SFTP_DESC,
+    CMD_STATUS,
+    SFTP_EOF,
+    CMD_NAME,
+    SFTP_BAD_MESSAGE,
+    CMD_EXTENDED_REPLY,
+    SFTP_FLAG_READ,
+    SFTP_FLAG_WRITE,
+    SFTP_FLAG_APPEND,
+    SFTP_FLAG_CREATE,
+    SFTP_FLAG_TRUNC,
+    SFTP_FLAG_EXCL,
+    CMD_NAMES,
+    CMD_OPEN,
+    CMD_CLOSE,
+    SFTP_OK,
+    CMD_READ,
+    CMD_DATA,
+    CMD_WRITE,
+    CMD_REMOVE,
+    CMD_RENAME,
+    CMD_MKDIR,
+    CMD_RMDIR,
+    CMD_OPENDIR,
+    CMD_READDIR,
+    CMD_STAT,
+    CMD_ATTRS,
+    CMD_LSTAT,
+    CMD_FSTAT,
+    CMD_SETSTAT,
+    CMD_FSETSTAT,
+    CMD_READLINK,
+    CMD_SYMLINK,
+    CMD_REALPATH,
+    CMD_EXTENDED,
+    SFTP_OP_UNSUPPORTED,
+)
+
+_hash_class = {"sha1": sha1, "md5": md5}
+
+
+class SFTPServer(BaseSFTP, SubsystemHandler):
+    """
+    Server-side SFTP subsystem support.  Since this is a `.SubsystemHandler`,
+    it can be (and is meant to be) set as the handler for ``"sftp"`` requests.
+    Use `.Transport.set_subsystem_handler` to activate this class.
+    """
+
+    def __init__(
+        self,
+        channel,
+        name,
+        server,
+        sftp_si=SFTPServerInterface,
+        *args,
+        **kwargs
+    ):
+        """
+        The constructor for SFTPServer is meant to be called from within the
+        `.Transport` as a subsystem handler.  ``server`` and any additional
+        parameters or keyword parameters are passed from the original call to
+        `.Transport.set_subsystem_handler`.
+
+        :param .Channel channel: channel passed from the `.Transport`.
+        :param str name: name of the requested subsystem.
+        :param .ServerInterface server:
+            the server object associated with this channel and subsystem
+        :param sftp_si:
+            a subclass of `.SFTPServerInterface` to use for handling individual
+            requests.
+        """
+        BaseSFTP.__init__(self)
+        SubsystemHandler.__init__(self, channel, name, server)
+        transport = channel.get_transport()
+        self.logger = util.get_logger(transport.get_log_channel() + ".sftp")
+        self.ultra_debug = transport.get_hexdump()
+        self.next_handle = 1
+        # map of handle-string to SFTPHandle for files & folders:
+        self.file_table = {}
+        self.folder_table = {}
+        self.server = sftp_si(server, *args, **kwargs)
+
+    def _log(self, level, msg):
+        if issubclass(type(msg), list):
+            for m in msg:
+                super()._log(level, "[chan " + self.sock.get_name() + "] " + m)
+        else:
+            super()._log(level, "[chan " + self.sock.get_name() + "] " + msg)
+
+    def start_subsystem(self, name, transport, channel):
+        self.sock = channel
+        self._log(DEBUG, "Started sftp server on channel {!r}".format(channel))
+        self._send_server_version()
+        self.server.session_started()
+        while True:
+            try:
+                t, data = self._read_packet()
+            except EOFError:
+                self._log(DEBUG, "EOF -- end of session")
+                return
+            except Exception as e:
+                self._log(DEBUG, "Exception on channel: " + str(e))
+                self._log(DEBUG, util.tb_strings())
+                return
+            msg = Message(data)
+            request_number = msg.get_int()
+            try:
+                self._process(t, request_number, msg)
+            except Exception as e:
+                self._log(DEBUG, "Exception in server processing: " + str(e))
+                self._log(DEBUG, util.tb_strings())
+                # send some kind of failure message, at least
+                try:
+                    self._send_status(request_number, SFTP_FAILURE)
+                except:
+                    pass
+
+    def finish_subsystem(self):
+        self.server.session_ended()
+        super().finish_subsystem()
+        # close any file handles that were left open
+        # (so we can return them to the OS quickly)
+        for f in self.file_table.values():
+            f.close()
+        for f in self.folder_table.values():
+            f.close()
+        self.file_table = {}
+        self.folder_table = {}
+
+    @staticmethod
+    def convert_errno(e):
+        """
+        Convert an errno value (as from an ``OSError`` or ``IOError``) into a
+        standard SFTP result code.  This is a convenience function for trapping
+        exceptions in server code and returning an appropriate result.
+
+        :param int e: an errno code, as from ``OSError.errno``.
+        :return: an `int` SFTP error code like ``SFTP_NO_SUCH_FILE``.
+        """
+        if e == errno.EACCES:
+            # permission denied
+            return SFTP_PERMISSION_DENIED
+        elif (e == errno.ENOENT) or (e == errno.ENOTDIR):
+            # no such file
+            return SFTP_NO_SUCH_FILE
+        else:
+            return SFTP_FAILURE
+
+    @staticmethod
+    def set_file_attr(filename, attr):
+        """
+        Change a file's attributes on the local filesystem.  The contents of
+        ``attr`` are used to change the permissions, owner, group ownership,
+        and/or modification & access time of the file, depending on which
+        attributes are present in ``attr``.
+
+        This is meant to be a handy helper function for translating SFTP file
+        requests into local file operations.
+
+        :param str filename:
+            name of the file to alter (should usually be an absolute path).
+        :param .SFTPAttributes attr: attributes to change.
+        """
+        if sys.platform != "win32":
+            # mode operations are meaningless on win32
+            if attr._flags & attr.FLAG_PERMISSIONS:
+                os.chmod(filename, attr.st_mode)
+            if attr._flags & attr.FLAG_UIDGID:
+                os.chown(filename, attr.st_uid, attr.st_gid)
+        if attr._flags & attr.FLAG_AMTIME:
+            os.utime(filename, (attr.st_atime, attr.st_mtime))
+        if attr._flags & attr.FLAG_SIZE:
+            with open(filename, "w+") as f:
+                f.truncate(attr.st_size)
+
+    # ...internals...
+
+    def _response(self, request_number, t, *args):
+        msg = Message()
+        msg.add_int(request_number)
+        for item in args:
+            # NOTE: this is a very silly tiny class used for SFTPFile mostly
+            if isinstance(item, int64):
+                msg.add_int64(item)
+            elif isinstance(item, int):
+                msg.add_int(item)
+            elif isinstance(item, (str, bytes)):
+                msg.add_string(item)
+            elif type(item) is SFTPAttributes:
+                item._pack(msg)
+            else:
+                raise Exception(
+                    "unknown type for {!r} type {!r}".format(item, type(item))
+                )
+        self._send_packet(t, msg)
+
+    def _send_handle_response(self, request_number, handle, folder=False):
+        if not issubclass(type(handle), SFTPHandle):
+            # must be error code
+            self._send_status(request_number, handle)
+            return
+        handle._set_name(b("hx{:d}".format(self.next_handle)))
+        self.next_handle += 1
+        if folder:
+            self.folder_table[handle._get_name()] = handle
+        else:
+            self.file_table[handle._get_name()] = handle
+        self._response(request_number, CMD_HANDLE, handle._get_name())
+
+    def _send_status(self, request_number, code, desc=None):
+        if desc is None:
+            try:
+                desc = SFTP_DESC[code]
+            except IndexError:
+                desc = "Unknown"
+        # some clients expect a "language" tag at the end
+        # (but don't mind it being blank)
+        self._response(request_number, CMD_STATUS, code, desc, "")
+
+    def _open_folder(self, request_number, path):
+        resp = self.server.list_folder(path)
+        if issubclass(type(resp), list):
+            # got an actual list of filenames in the folder
+            folder = SFTPHandle()
+            folder._set_files(resp)
+            self._send_handle_response(request_number, folder, True)
+            return
+        # must be an error code
+        self._send_status(request_number, resp)
+
+    def _read_folder(self, request_number, folder):
+        flist = folder._get_next_files()
+        if len(flist) == 0:
+            self._send_status(request_number, SFTP_EOF)
+            return
+        msg = Message()
+        msg.add_int(request_number)
+        msg.add_int(len(flist))
+        for attr in flist:
+            msg.add_string(attr.filename)
+            msg.add_string(attr)
+            attr._pack(msg)
+        self._send_packet(CMD_NAME, msg)
+
+    def _check_file(self, request_number, msg):
+        # this extension actually comes from v6 protocol, but since it's an
+        # extension, i feel like we can reasonably support it backported.
+        # it's very useful for verifying uploaded files or checking for
+        # rsync-like differences between local and remote files.
+        handle = msg.get_binary()
+        alg_list = msg.get_list()
+        start = msg.get_int64()
+        length = msg.get_int64()
+        block_size = msg.get_int()
+        if handle not in self.file_table:
+            self._send_status(
+                request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+            )
+            return
+        f = self.file_table[handle]
+        for x in alg_list:
+            if x in _hash_class:
+                algname = x
+                alg = _hash_class[x]
+                break
+        else:
+            self._send_status(
+                request_number, SFTP_FAILURE, "No supported hash types found"
+            )
+            return
+        if length == 0:
+            st = f.stat()
+            if not issubclass(type(st), SFTPAttributes):
+                self._send_status(request_number, st, "Unable to stat file")
+                return
+            length = st.st_size - start
+        if block_size == 0:
+            block_size = length
+        if block_size < 256:
+            self._send_status(
+                request_number, SFTP_FAILURE, "Block size too small"
+            )
+            return
+
+        sum_out = bytes()
+        offset = start
+        while offset < start + length:
+            blocklen = min(block_size, start + length - offset)
+            # don't try to read more than about 64KB at a time
+            chunklen = min(blocklen, 65536)
+            count = 0
+            hash_obj = alg()
+            while count < blocklen:
+                data = f.read(offset, chunklen)
+                if not isinstance(data, bytes):
+                    self._send_status(
+                        request_number, data, "Unable to hash file"
+                    )
+                    return
+                hash_obj.update(data)
+                count += len(data)
+                offset += count
+            sum_out += hash_obj.digest()
+
+        msg = Message()
+        msg.add_int(request_number)
+        msg.add_string("check-file")
+        msg.add_string(algname)
+        msg.add_bytes(sum_out)
+        self._send_packet(CMD_EXTENDED_REPLY, msg)
+
+    def _convert_pflags(self, pflags):
+        """convert SFTP-style open() flags to Python's os.open() flags"""
+        if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE):
+            flags = os.O_RDWR
+        elif pflags & SFTP_FLAG_WRITE:
+            flags = os.O_WRONLY
+        else:
+            flags = os.O_RDONLY
+        if pflags & SFTP_FLAG_APPEND:
+            flags |= os.O_APPEND
+        if pflags & SFTP_FLAG_CREATE:
+            flags |= os.O_CREAT
+        if pflags & SFTP_FLAG_TRUNC:
+            flags |= os.O_TRUNC
+        if pflags & SFTP_FLAG_EXCL:
+            flags |= os.O_EXCL
+        return flags
+
+    def _process(self, t, request_number, msg):
+        self._log(DEBUG, "Request: {}".format(CMD_NAMES[t]))
+        if t == CMD_OPEN:
+            path = msg.get_text()
+            flags = self._convert_pflags(msg.get_int())
+            attr = SFTPAttributes._from_msg(msg)
+            self._send_handle_response(
+                request_number, self.server.open(path, flags, attr)
+            )
+        elif t == CMD_CLOSE:
+            handle = msg.get_binary()
+            if handle in self.folder_table:
+                del self.folder_table[handle]
+                self._send_status(request_number, SFTP_OK)
+                return
+            if handle in self.file_table:
+                self.file_table[handle].close()
+                del self.file_table[handle]
+                self._send_status(request_number, SFTP_OK)
+                return
+            self._send_status(
+                request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+            )
+        elif t == CMD_READ:
+            handle = msg.get_binary()
+            offset = msg.get_int64()
+            length = msg.get_int()
+            if handle not in self.file_table:
+                self._send_status(
+                    request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+                )
+                return
+            data = self.file_table[handle].read(offset, length)
+            if isinstance(data, (bytes, str)):
+                if len(data) == 0:
+                    self._send_status(request_number, SFTP_EOF)
+                else:
+                    self._response(request_number, CMD_DATA, data)
+            else:
+                self._send_status(request_number, data)
+        elif t == CMD_WRITE:
+            handle = msg.get_binary()
+            offset = msg.get_int64()
+            data = msg.get_binary()
+            if handle not in self.file_table:
+                self._send_status(
+                    request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+                )
+                return
+            self._send_status(
+                request_number, self.file_table[handle].write(offset, data)
+            )
+        elif t == CMD_REMOVE:
+            path = msg.get_text()
+            self._send_status(request_number, self.server.remove(path))
+        elif t == CMD_RENAME:
+            oldpath = msg.get_text()
+            newpath = msg.get_text()
+            self._send_status(
+                request_number, self.server.rename(oldpath, newpath)
+            )
+        elif t == CMD_MKDIR:
+            path = msg.get_text()
+            attr = SFTPAttributes._from_msg(msg)
+            self._send_status(request_number, self.server.mkdir(path, attr))
+        elif t == CMD_RMDIR:
+            path = msg.get_text()
+            self._send_status(request_number, self.server.rmdir(path))
+        elif t == CMD_OPENDIR:
+            path = msg.get_text()
+            self._open_folder(request_number, path)
+            return
+        elif t == CMD_READDIR:
+            handle = msg.get_binary()
+            if handle not in self.folder_table:
+                self._send_status(
+                    request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+                )
+                return
+            folder = self.folder_table[handle]
+            self._read_folder(request_number, folder)
+        elif t == CMD_STAT:
+            path = msg.get_text()
+            resp = self.server.stat(path)
+            if issubclass(type(resp), SFTPAttributes):
+                self._response(request_number, CMD_ATTRS, resp)
+            else:
+                self._send_status(request_number, resp)
+        elif t == CMD_LSTAT:
+            path = msg.get_text()
+            resp = self.server.lstat(path)
+            if issubclass(type(resp), SFTPAttributes):
+                self._response(request_number, CMD_ATTRS, resp)
+            else:
+                self._send_status(request_number, resp)
+        elif t == CMD_FSTAT:
+            handle = msg.get_binary()
+            if handle not in self.file_table:
+                self._send_status(
+                    request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+                )
+                return
+            resp = self.file_table[handle].stat()
+            if issubclass(type(resp), SFTPAttributes):
+                self._response(request_number, CMD_ATTRS, resp)
+            else:
+                self._send_status(request_number, resp)
+        elif t == CMD_SETSTAT:
+            path = msg.get_text()
+            attr = SFTPAttributes._from_msg(msg)
+            self._send_status(request_number, self.server.chattr(path, attr))
+        elif t == CMD_FSETSTAT:
+            handle = msg.get_binary()
+            attr = SFTPAttributes._from_msg(msg)
+            if handle not in self.file_table:
+                self._response(
+                    request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+                )
+                return
+            self._send_status(
+                request_number, self.file_table[handle].chattr(attr)
+            )
+        elif t == CMD_READLINK:
+            path = msg.get_text()
+            resp = self.server.readlink(path)
+            if isinstance(resp, (bytes, str)):
+                self._response(
+                    request_number, CMD_NAME, 1, resp, "", SFTPAttributes()
+                )
+            else:
+                self._send_status(request_number, resp)
+        elif t == CMD_SYMLINK:
+            # the sftp 2 draft is incorrect here!
+            # path always follows target_path
+            target_path = msg.get_text()
+            path = msg.get_text()
+            self._send_status(
+                request_number, self.server.symlink(target_path, path)
+            )
+        elif t == CMD_REALPATH:
+            path = msg.get_text()
+            rpath = self.server.canonicalize(path)
+            self._response(
+                request_number, CMD_NAME, 1, rpath, "", SFTPAttributes()
+            )
+        elif t == CMD_EXTENDED:
+            tag = msg.get_text()
+            if tag == "check-file":
+                self._check_file(request_number, msg)
+            elif tag == "posix-rename@openssh.com":
+                oldpath = msg.get_text()
+                newpath = msg.get_text()
+                self._send_status(
+                    request_number, self.server.posix_rename(oldpath, newpath)
+                )
+            else:
+                self._send_status(request_number, SFTP_OP_UNSUPPORTED)
+        else:
+            self._send_status(request_number, SFTP_OP_UNSUPPORTED)
+
+
+from paramiko.sftp_handle import SFTPHandle
diff --git a/paramiko/sftp_si.py b/paramiko/sftp_si.py
new file mode 100644
index 0000000..72b5db9
--- /dev/null
+++ b/paramiko/sftp_si.py
@@ -0,0 +1,316 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+An interface to override for SFTP server support.
+"""
+
+import os
+import sys
+from paramiko.sftp import SFTP_OP_UNSUPPORTED
+
+
+class SFTPServerInterface:
+    """
+    This class defines an interface for controlling the behavior of paramiko
+    when using the `.SFTPServer` subsystem to provide an SFTP server.
+
+    Methods on this class are called from the SFTP session's thread, so you can
+    block as long as necessary without affecting other sessions (even other
+    SFTP sessions).  However, raising an exception will usually cause the SFTP
+    session to abruptly end, so you will usually want to catch exceptions and
+    return an appropriate error code.
+
+    All paths are in string form instead of unicode because not all SFTP
+    clients & servers obey the requirement that paths be encoded in UTF-8.
+    """
+
+    def __init__(self, server, *args, **kwargs):
+        """
+        Create a new SFTPServerInterface object.  This method does nothing by
+        default and is meant to be overridden by subclasses.
+
+        :param .ServerInterface server:
+            the server object associated with this channel and SFTP subsystem
+        """
+        super().__init__(*args, **kwargs)
+
+    def session_started(self):
+        """
+        The SFTP server session has just started.  This method is meant to be
+        overridden to perform any necessary setup before handling callbacks
+        from SFTP operations.
+        """
+        pass
+
+    def session_ended(self):
+        """
+        The SFTP server session has just ended, either cleanly or via an
+        exception.  This method is meant to be overridden to perform any
+        necessary cleanup before this `.SFTPServerInterface` object is
+        destroyed.
+        """
+        pass
+
+    def open(self, path, flags, attr):
+        """
+        Open a file on the server and create a handle for future operations
+        on that file.  On success, a new object subclassed from `.SFTPHandle`
+        should be returned.  This handle will be used for future operations
+        on the file (read, write, etc).  On failure, an error code such as
+        ``SFTP_PERMISSION_DENIED`` should be returned.
+
+        ``flags`` contains the requested mode for opening (read-only,
+        write-append, etc) as a bitset of flags from the ``os`` module:
+
+            - ``os.O_RDONLY``
+            - ``os.O_WRONLY``
+            - ``os.O_RDWR``
+            - ``os.O_APPEND``
+            - ``os.O_CREAT``
+            - ``os.O_TRUNC``
+            - ``os.O_EXCL``
+
+        (One of ``os.O_RDONLY``, ``os.O_WRONLY``, or ``os.O_RDWR`` will always
+        be set.)
+
+        The ``attr`` object contains requested attributes of the file if it
+        has to be created.  Some or all attribute fields may be missing if
+        the client didn't specify them.
+
+        .. note:: The SFTP protocol defines all files to be in "binary" mode.
+            There is no equivalent to Python's "text" mode.
+
+        :param str path:
+            the requested path (relative or absolute) of the file to be opened.
+        :param int flags:
+            flags or'd together from the ``os`` module indicating the requested
+            mode for opening the file.
+        :param .SFTPAttributes attr:
+            requested attributes of the file if it is newly created.
+        :return: a new `.SFTPHandle` or error code.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def list_folder(self, path):
+        """
+        Return a list of files within a given folder.  The ``path`` will use
+        posix notation (``"/"`` separates folder names) and may be an absolute
+        or relative path.
+
+        The list of files is expected to be a list of `.SFTPAttributes`
+        objects, which are similar in structure to the objects returned by
+        ``os.stat``.  In addition, each object should have its ``filename``
+        field filled in, since this is important to a directory listing and
+        not normally present in ``os.stat`` results.  The method
+        `.SFTPAttributes.from_stat` will usually do what you want.
+
+        In case of an error, you should return one of the ``SFTP_*`` error
+        codes, such as ``SFTP_PERMISSION_DENIED``.
+
+        :param str path: the requested path (relative or absolute) to be
+            listed.
+        :return:
+            a list of the files in the given folder, using `.SFTPAttributes`
+            objects.
+
+        .. note::
+            You should normalize the given ``path`` first (see the `os.path`
+            module) and check appropriate permissions before returning the list
+            of files.  Be careful of malicious clients attempting to use
+            relative paths to escape restricted folders, if you're doing a
+            direct translation from the SFTP server path to your local
+            filesystem.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def stat(self, path):
+        """
+        Return an `.SFTPAttributes` object for a path on the server, or an
+        error code.  If your server supports symbolic links (also known as
+        "aliases"), you should follow them.  (`lstat` is the corresponding
+        call that doesn't follow symlinks/aliases.)
+
+        :param str path:
+            the requested path (relative or absolute) to fetch file statistics
+            for.
+        :return:
+            an `.SFTPAttributes` object for the given file, or an SFTP error
+            code (like ``SFTP_PERMISSION_DENIED``).
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def lstat(self, path):
+        """
+        Return an `.SFTPAttributes` object for a path on the server, or an
+        error code.  If your server supports symbolic links (also known as
+        "aliases"), you should not follow them -- instead, you should
+        return data on the symlink or alias itself.  (`stat` is the
+        corresponding call that follows symlinks/aliases.)
+
+        :param str path:
+            the requested path (relative or absolute) to fetch file statistics
+            for.
+        :type path: str
+        :return:
+            an `.SFTPAttributes` object for the given file, or an SFTP error
+            code (like ``SFTP_PERMISSION_DENIED``).
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def remove(self, path):
+        """
+        Delete a file, if possible.
+
+        :param str path:
+            the requested path (relative or absolute) of the file to delete.
+        :return: an SFTP error code `int` like ``SFTP_OK``.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def rename(self, oldpath, newpath):
+        """
+        Rename (or move) a file.  The SFTP specification implies that this
+        method can be used to move an existing file into a different folder,
+        and since there's no other (easy) way to move files via SFTP, it's
+        probably a good idea to implement "move" in this method too, even for
+        files that cross disk partition boundaries, if at all possible.
+
+        .. note:: You should return an error if a file with the same name as
+            ``newpath`` already exists.  (The rename operation should be
+            non-desctructive.)
+
+        .. note::
+            This method implements 'standard' SFTP ``RENAME`` behavior; those
+            seeking the OpenSSH "POSIX rename" extension behavior should use
+            `posix_rename`.
+
+        :param str oldpath:
+            the requested path (relative or absolute) of the existing file.
+        :param str newpath: the requested new path of the file.
+        :return: an SFTP error code `int` like ``SFTP_OK``.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def posix_rename(self, oldpath, newpath):
+        """
+        Rename (or move) a file, following posix conventions. If newpath
+        already exists, it will be overwritten.
+
+        :param str oldpath:
+            the requested path (relative or absolute) of the existing file.
+        :param str newpath: the requested new path of the file.
+        :return: an SFTP error code `int` like ``SFTP_OK``.
+
+        :versionadded: 2.2
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def mkdir(self, path, attr):
+        """
+        Create a new directory with the given attributes.  The ``attr``
+        object may be considered a "hint" and ignored.
+
+        The ``attr`` object will contain only those fields provided by the
+        client in its request, so you should use ``hasattr`` to check for
+        the presence of fields before using them.  In some cases, the ``attr``
+        object may be completely empty.
+
+        :param str path:
+            requested path (relative or absolute) of the new folder.
+        :param .SFTPAttributes attr: requested attributes of the new folder.
+        :return: an SFTP error code `int` like ``SFTP_OK``.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def rmdir(self, path):
+        """
+        Remove a directory if it exists.  The ``path`` should refer to an
+        existing, empty folder -- otherwise this method should return an
+        error.
+
+        :param str path:
+            requested path (relative or absolute) of the folder to remove.
+        :return: an SFTP error code `int` like ``SFTP_OK``.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def chattr(self, path, attr):
+        """
+        Change the attributes of a file.  The ``attr`` object will contain
+        only those fields provided by the client in its request, so you
+        should check for the presence of fields before using them.
+
+        :param str path:
+            requested path (relative or absolute) of the file to change.
+        :param attr:
+            requested attributes to change on the file (an `.SFTPAttributes`
+            object)
+        :return: an error code `int` like ``SFTP_OK``.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def canonicalize(self, path):
+        """
+        Return the canonical form of a path on the server.  For example,
+        if the server's home folder is ``/home/foo``, the path
+        ``"../betty"`` would be canonicalized to ``"/home/betty"``.  Note
+        the obvious security issues: if you're serving files only from a
+        specific folder, you probably don't want this method to reveal path
+        names outside that folder.
+
+        You may find the Python methods in ``os.path`` useful, especially
+        ``os.path.normpath`` and ``os.path.realpath``.
+
+        The default implementation returns ``os.path.normpath('/' + path)``.
+        """
+        if os.path.isabs(path):
+            out = os.path.normpath(path)
+        else:
+            out = os.path.normpath("/" + path)
+        if sys.platform == "win32":
+            # on windows, normalize backslashes to sftp/posix format
+            out = out.replace("\\", "/")
+        return out
+
+    def readlink(self, path):
+        """
+        Return the target of a symbolic link (or shortcut) on the server.
+        If the specified path doesn't refer to a symbolic link, an error
+        should be returned.
+
+        :param str path: path (relative or absolute) of the symbolic link.
+        :return:
+            the target `str` path of the symbolic link, or an error code like
+            ``SFTP_NO_SUCH_FILE``.
+        """
+        return SFTP_OP_UNSUPPORTED
+
+    def symlink(self, target_path, path):
+        """
+        Create a symbolic link on the server, as new pathname ``path``,
+        with ``target_path`` as the target of the link.
+
+        :param str target_path:
+            path (relative or absolute) of the target for this new symbolic
+            link.
+        :param str path:
+            path (relative or absolute) of the symbolic link to create.
+        :return: an error code `int` like ``SFTP_OK``.
+        """
+        return SFTP_OP_UNSUPPORTED
diff --git a/paramiko/ssh_exception.py b/paramiko/ssh_exception.py
new file mode 100644
index 0000000..9b1b44c
--- /dev/null
+++ b/paramiko/ssh_exception.py
@@ -0,0 +1,235 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+import socket
+
+
+class SSHException(Exception):
+    """
+    Exception raised by failures in SSH2 protocol negotiation or logic errors.
+    """
+
+    pass
+
+
+class AuthenticationException(SSHException):
+    """
+    Exception raised when authentication failed for some reason.  It may be
+    possible to retry with different credentials.  (Other classes specify more
+    specific reasons.)
+
+    .. versionadded:: 1.6
+    """
+
+    pass
+
+
+class PasswordRequiredException(AuthenticationException):
+    """
+    Exception raised when a password is needed to unlock a private key file.
+    """
+
+    pass
+
+
+class BadAuthenticationType(AuthenticationException):
+    """
+    Exception raised when an authentication type (like password) is used, but
+    the server isn't allowing that type.  (It may only allow public-key, for
+    example.)
+
+    .. versionadded:: 1.1
+    """
+
+    allowed_types = []
+
+    # TODO 4.0: remove explanation kwarg
+    def __init__(self, explanation, types):
+        # TODO 4.0: remove this supercall unless it's actually required for
+        # pickling (after fixing pickling)
+        AuthenticationException.__init__(self, explanation, types)
+        self.explanation = explanation
+        self.allowed_types = types
+
+    def __str__(self):
+        return "{}; allowed types: {!r}".format(
+            self.explanation, self.allowed_types
+        )
+
+
+class PartialAuthentication(AuthenticationException):
+    """
+    An internal exception thrown in the case of partial authentication.
+    """
+
+    allowed_types = []
+
+    def __init__(self, types):
+        AuthenticationException.__init__(self, types)
+        self.allowed_types = types
+
+    def __str__(self):
+        return "Partial authentication; allowed types: {!r}".format(
+            self.allowed_types
+        )
+
+
+class ChannelException(SSHException):
+    """
+    Exception raised when an attempt to open a new `.Channel` fails.
+
+    :param int code: the error code returned by the server
+
+    .. versionadded:: 1.6
+    """
+
+    def __init__(self, code, text):
+        SSHException.__init__(self, code, text)
+        self.code = code
+        self.text = text
+
+    def __str__(self):
+        return "ChannelException({!r}, {!r})".format(self.code, self.text)
+
+
+class BadHostKeyException(SSHException):
+    """
+    The host key given by the SSH server did not match what we were expecting.
+
+    :param str hostname: the hostname of the SSH server
+    :param PKey got_key: the host key presented by the server
+    :param PKey expected_key: the host key expected
+
+    .. versionadded:: 1.6
+    """
+
+    def __init__(self, hostname, got_key, expected_key):
+        SSHException.__init__(self, hostname, got_key, expected_key)
+        self.hostname = hostname
+        self.key = got_key
+        self.expected_key = expected_key
+
+    def __str__(self):
+        msg = "Host key for server '{}' does not match: got '{}', expected '{}'"  # noqa
+        return msg.format(
+            self.hostname,
+            self.key.get_base64(),
+            self.expected_key.get_base64(),
+        )
+
+
+class IncompatiblePeer(SSHException):
+    """
+    A disagreement arose regarding an algorithm required for key exchange.
+
+    .. versionadded:: 2.9
+    """
+
+    # TODO 4.0: consider making this annotate w/ 1..N 'missing' algorithms,
+    # either just the first one that would halt kex, or even updating the
+    # Transport logic so we record /all/ that /could/ halt kex.
+    # TODO: update docstrings where this may end up raised so they are more
+    # specific.
+    pass
+
+
+class ProxyCommandFailure(SSHException):
+    """
+    The "ProxyCommand" found in the .ssh/config file returned an error.
+
+    :param str command: The command line that is generating this exception.
+    :param str error: The error captured from the proxy command output.
+    """
+
+    def __init__(self, command, error):
+        SSHException.__init__(self, command, error)
+        self.command = command
+        self.error = error
+
+    def __str__(self):
+        return 'ProxyCommand("{}") returned nonzero exit status: {}'.format(
+            self.command, self.error
+        )
+
+
+class NoValidConnectionsError(socket.error):
+    """
+    Multiple connection attempts were made and no families succeeded.
+
+    This exception class wraps multiple "real" underlying connection errors,
+    all of which represent failed connection attempts. Because these errors are
+    not guaranteed to all be of the same error type (i.e. different errno,
+    `socket.error` subclass, message, etc) we expose a single unified error
+    message and a ``None`` errno so that instances of this class match most
+    normal handling of `socket.error` objects.
+
+    To see the wrapped exception objects, access the ``errors`` attribute.
+    ``errors`` is a dict whose keys are address tuples (e.g. ``('127.0.0.1',
+    22)``) and whose values are the exception encountered trying to connect to
+    that address.
+
+    It is implied/assumed that all the errors given to a single instance of
+    this class are from connecting to the same hostname + port (and thus that
+    the differences are in the resolution of the hostname - e.g. IPv4 vs v6).
+
+    .. versionadded:: 1.16
+    """
+
+    def __init__(self, errors):
+        """
+        :param dict errors:
+            The errors dict to store, as described by class docstring.
+        """
+        addrs = sorted(errors.keys())
+        body = ", ".join([x[0] for x in addrs[:-1]])
+        tail = addrs[-1][0]
+        if body:
+            msg = "Unable to connect to port {0} on {1} or {2}"
+        else:
+            msg = "Unable to connect to port {0} on {2}"
+        super().__init__(
+            None, msg.format(addrs[0][1], body, tail)  # stand-in for errno
+        )
+        self.errors = errors
+
+    def __reduce__(self):
+        return (self.__class__, (self.errors,))
+
+
+class CouldNotCanonicalize(SSHException):
+    """
+    Raised when hostname canonicalization fails & fallback is disabled.
+
+    .. versionadded:: 2.7
+    """
+
+    pass
+
+
+class ConfigParseError(SSHException):
+    """
+    A fatal error was encountered trying to parse SSH config data.
+
+    Typically this means a config file violated the ``ssh_config``
+    specification in a manner that requires exiting immediately, such as not
+    matching ``key = value`` syntax or misusing certain ``Match`` keywords.
+
+    .. versionadded:: 2.7
+    """
+
+    pass
diff --git a/paramiko/ssh_gss.py b/paramiko/ssh_gss.py
new file mode 100644
index 0000000..ee49c34
--- /dev/null
+++ b/paramiko/ssh_gss.py
@@ -0,0 +1,778 @@
+# Copyright (C) 2013-2014 science + computing ag
+# Author: Sebastian Deiss <sebastian.deiss@t-online.de>
+#
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+
+"""
+This module provides GSS-API / SSPI  authentication as defined in :rfc:`4462`.
+
+.. note:: Credential delegation is not supported in server mode.
+
+.. seealso:: :doc:`/api/kex_gss`
+
+.. versionadded:: 1.15
+"""
+
+import struct
+import os
+import sys
+
+
+#: A boolean constraint that indicates if GSS-API / SSPI is available.
+GSS_AUTH_AVAILABLE = True
+
+
+#: A tuple of the exception types used by the underlying GSSAPI implementation.
+GSS_EXCEPTIONS = ()
+
+
+#: :var str _API: Constraint for the used API
+_API = None
+
+try:
+    import gssapi
+
+    if hasattr(gssapi, "__title__") and gssapi.__title__ == "python-gssapi":
+        # old, unmaintained python-gssapi package
+        _API = "MIT"  # keep this for compatibility
+        GSS_EXCEPTIONS = (gssapi.GSSException,)
+    else:
+        _API = "PYTHON-GSSAPI-NEW"
+        GSS_EXCEPTIONS = (
+            gssapi.exceptions.GeneralError,
+            gssapi.raw.misc.GSSError,
+        )
+except (ImportError, OSError):
+    try:
+        import pywintypes
+        import sspicon
+        import sspi
+
+        _API = "SSPI"
+        GSS_EXCEPTIONS = (pywintypes.error,)
+    except ImportError:
+        GSS_AUTH_AVAILABLE = False
+        _API = None
+
+from paramiko.common import MSG_USERAUTH_REQUEST
+from paramiko.ssh_exception import SSHException
+from paramiko._version import __version_info__
+
+
+def GSSAuth(auth_method, gss_deleg_creds=True):
+    """
+    Provide SSH2 GSS-API / SSPI authentication.
+
+    :param str auth_method: The name of the SSH authentication mechanism
+                            (gssapi-with-mic or gss-keyex)
+    :param bool gss_deleg_creds: Delegate client credentials or not.
+                                 We delegate credentials by default.
+    :return: Either an `._SSH_GSSAPI_OLD` or `._SSH_GSSAPI_NEW` (Unix)
+             object or an `_SSH_SSPI` (Windows) object
+    :rtype: object
+
+    :raises: ``ImportError`` -- If no GSS-API / SSPI module could be imported.
+
+    :see: `RFC 4462 <http://www.ietf.org/rfc/rfc4462.txt>`_
+    :note: Check for the available API and return either an `._SSH_GSSAPI_OLD`
+           (MIT GSSAPI using python-gssapi package) object, an
+           `._SSH_GSSAPI_NEW` (MIT GSSAPI using gssapi package) object
+           or an `._SSH_SSPI` (MS SSPI) object.
+           If there is no supported API available,
+           ``None`` will be returned.
+    """
+    if _API == "MIT":
+        return _SSH_GSSAPI_OLD(auth_method, gss_deleg_creds)
+    elif _API == "PYTHON-GSSAPI-NEW":
+        return _SSH_GSSAPI_NEW(auth_method, gss_deleg_creds)
+    elif _API == "SSPI" and os.name == "nt":
+        return _SSH_SSPI(auth_method, gss_deleg_creds)
+    else:
+        raise ImportError("Unable to import a GSS-API / SSPI module!")
+
+
+class _SSH_GSSAuth:
+    """
+    Contains the shared variables and methods of `._SSH_GSSAPI_OLD`,
+    `._SSH_GSSAPI_NEW` and `._SSH_SSPI`.
+    """
+
+    def __init__(self, auth_method, gss_deleg_creds):
+        """
+        :param str auth_method: The name of the SSH authentication mechanism
+                                (gssapi-with-mic or gss-keyex)
+        :param bool gss_deleg_creds: Delegate client credentials or not
+        """
+        self._auth_method = auth_method
+        self._gss_deleg_creds = gss_deleg_creds
+        self._gss_host = None
+        self._username = None
+        self._session_id = None
+        self._service = "ssh-connection"
+        """
+        OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,
+        so we also support the krb5 mechanism only.
+        """
+        self._krb5_mech = "1.2.840.113554.1.2.2"
+
+        # client mode
+        self._gss_ctxt = None
+        self._gss_ctxt_status = False
+
+        # server mode
+        self._gss_srv_ctxt = None
+        self._gss_srv_ctxt_status = False
+        self.cc_file = None
+
+    def set_service(self, service):
+        """
+        This is just a setter to use a non default service.
+        I added this method, because RFC 4462 doesn't specify "ssh-connection"
+        as the only service value.
+
+        :param str service: The desired SSH service
+        """
+        if service.find("ssh-"):
+            self._service = service
+
+    def set_username(self, username):
+        """
+        Setter for C{username}. If GSS-API Key Exchange is performed, the
+        username is not set by C{ssh_init_sec_context}.
+
+        :param str username: The name of the user who attempts to login
+        """
+        self._username = username
+
+    def ssh_gss_oids(self, mode="client"):
+        """
+        This method returns a single OID, because we only support the
+        Kerberos V5 mechanism.
+
+        :param str mode: Client for client mode and server for server mode
+        :return: A byte sequence containing the number of supported
+                 OIDs, the length of the OID and the actual OID encoded with
+                 DER
+        :note: In server mode we just return the OID length and the DER encoded
+               OID.
+        """
+        from pyasn1.type.univ import ObjectIdentifier
+        from pyasn1.codec.der import encoder
+
+        OIDs = self._make_uint32(1)
+        krb5_OID = encoder.encode(ObjectIdentifier(self._krb5_mech))
+        OID_len = self._make_uint32(len(krb5_OID))
+        if mode == "server":
+            return OID_len + krb5_OID
+        return OIDs + OID_len + krb5_OID
+
+    def ssh_check_mech(self, desired_mech):
+        """
+        Check if the given OID is the Kerberos V5 OID (server mode).
+
+        :param str desired_mech: The desired GSS-API mechanism of the client
+        :return: ``True`` if the given OID is supported, otherwise C{False}
+        """
+        from pyasn1.codec.der import decoder
+
+        mech, __ = decoder.decode(desired_mech)
+        if mech.__str__() != self._krb5_mech:
+            return False
+        return True
+
+    # Internals
+    # -------------------------------------------------------------------------
+    def _make_uint32(self, integer):
+        """
+        Create a 32 bit unsigned integer (The byte sequence of an integer).
+
+        :param int integer: The integer value to convert
+        :return: The byte sequence of an 32 bit integer
+        """
+        return struct.pack("!I", integer)
+
+    def _ssh_build_mic(self, session_id, username, service, auth_method):
+        """
+        Create the SSH2 MIC filed for gssapi-with-mic.
+
+        :param str session_id: The SSH session ID
+        :param str username: The name of the user who attempts to login
+        :param str service: The requested SSH service
+        :param str auth_method: The requested SSH authentication mechanism
+        :return: The MIC as defined in RFC 4462. The contents of the
+                 MIC field are:
+                 string    session_identifier,
+                 byte      SSH_MSG_USERAUTH_REQUEST,
+                 string    user-name,
+                 string    service (ssh-connection),
+                 string    authentication-method
+                           (gssapi-with-mic or gssapi-keyex)
+        """
+        mic = self._make_uint32(len(session_id))
+        mic += session_id
+        mic += struct.pack("B", MSG_USERAUTH_REQUEST)
+        mic += self._make_uint32(len(username))
+        mic += username.encode()
+        mic += self._make_uint32(len(service))
+        mic += service.encode()
+        mic += self._make_uint32(len(auth_method))
+        mic += auth_method.encode()
+        return mic
+
+
+class _SSH_GSSAPI_OLD(_SSH_GSSAuth):
+    """
+    Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
+    using the older (unmaintained) python-gssapi package.
+
+    :see: `.GSSAuth`
+    """
+
+    def __init__(self, auth_method, gss_deleg_creds):
+        """
+        :param str auth_method: The name of the SSH authentication mechanism
+                                (gssapi-with-mic or gss-keyex)
+        :param bool gss_deleg_creds: Delegate client credentials or not
+        """
+        _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
+
+        if self._gss_deleg_creds:
+            self._gss_flags = (
+                gssapi.C_PROT_READY_FLAG,
+                gssapi.C_INTEG_FLAG,
+                gssapi.C_MUTUAL_FLAG,
+                gssapi.C_DELEG_FLAG,
+            )
+        else:
+            self._gss_flags = (
+                gssapi.C_PROT_READY_FLAG,
+                gssapi.C_INTEG_FLAG,
+                gssapi.C_MUTUAL_FLAG,
+            )
+
+    def ssh_init_sec_context(
+        self, target, desired_mech=None, username=None, recv_token=None
+    ):
+        """
+        Initialize a GSS-API context.
+
+        :param str username: The name of the user who attempts to login
+        :param str target: The hostname of the target to connect to
+        :param str desired_mech: The negotiated GSS-API mechanism
+                                 ("pseudo negotiated" mechanism, because we
+                                 support just the krb5 mechanism :-))
+        :param str recv_token: The GSS-API token received from the Server
+        :raises:
+            `.SSHException` -- Is raised if the desired mechanism of the client
+            is not supported
+        :return: A ``String`` if the GSS-API has returned a token or
+            ``None`` if no token was returned
+        """
+        from pyasn1.codec.der import decoder
+
+        self._username = username
+        self._gss_host = target
+        targ_name = gssapi.Name(
+            "host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE
+        )
+        ctx = gssapi.Context()
+        ctx.flags = self._gss_flags
+        if desired_mech is None:
+            krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
+        else:
+            mech, __ = decoder.decode(desired_mech)
+            if mech.__str__() != self._krb5_mech:
+                raise SSHException("Unsupported mechanism OID.")
+            else:
+                krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
+        token = None
+        try:
+            if recv_token is None:
+                self._gss_ctxt = gssapi.InitContext(
+                    peer_name=targ_name,
+                    mech_type=krb5_mech,
+                    req_flags=ctx.flags,
+                )
+                token = self._gss_ctxt.step(token)
+            else:
+                token = self._gss_ctxt.step(recv_token)
+        except gssapi.GSSException:
+            message = "{} Target: {}".format(sys.exc_info()[1], self._gss_host)
+            raise gssapi.GSSException(message)
+        self._gss_ctxt_status = self._gss_ctxt.established
+        return token
+
+    def ssh_get_mic(self, session_id, gss_kex=False):
+        """
+        Create the MIC token for a SSH2 message.
+
+        :param str session_id: The SSH session ID
+        :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
+        :return: gssapi-with-mic:
+                 Returns the MIC token from GSS-API for the message we created
+                 with ``_ssh_build_mic``.
+                 gssapi-keyex:
+                 Returns the MIC token from GSS-API with the SSH session ID as
+                 message.
+        """
+        self._session_id = session_id
+        if not gss_kex:
+            mic_field = self._ssh_build_mic(
+                self._session_id,
+                self._username,
+                self._service,
+                self._auth_method,
+            )
+            mic_token = self._gss_ctxt.get_mic(mic_field)
+        else:
+            # for key exchange with gssapi-keyex
+            mic_token = self._gss_srv_ctxt.get_mic(self._session_id)
+        return mic_token
+
+    def ssh_accept_sec_context(self, hostname, recv_token, username=None):
+        """
+        Accept a GSS-API context (server mode).
+
+        :param str hostname: The servers hostname
+        :param str username: The name of the user who attempts to login
+        :param str recv_token: The GSS-API Token received from the server,
+                               if it's not the initial call.
+        :return: A ``String`` if the GSS-API has returned a token or ``None``
+                if no token was returned
+        """
+        # hostname and username are not required for GSSAPI, but for SSPI
+        self._gss_host = hostname
+        self._username = username
+        if self._gss_srv_ctxt is None:
+            self._gss_srv_ctxt = gssapi.AcceptContext()
+        token = self._gss_srv_ctxt.step(recv_token)
+        self._gss_srv_ctxt_status = self._gss_srv_ctxt.established
+        return token
+
+    def ssh_check_mic(self, mic_token, session_id, username=None):
+        """
+        Verify the MIC token for a SSH2 message.
+
+        :param str mic_token: The MIC token received from the client
+        :param str session_id: The SSH session ID
+        :param str username: The name of the user who attempts to login
+        :return: None if the MIC check was successful
+        :raises: ``gssapi.GSSException`` -- if the MIC check failed
+        """
+        self._session_id = session_id
+        self._username = username
+        if self._username is not None:
+            # server mode
+            mic_field = self._ssh_build_mic(
+                self._session_id,
+                self._username,
+                self._service,
+                self._auth_method,
+            )
+            self._gss_srv_ctxt.verify_mic(mic_field, mic_token)
+        else:
+            # for key exchange with gssapi-keyex
+            # client mode
+            self._gss_ctxt.verify_mic(self._session_id, mic_token)
+
+    @property
+    def credentials_delegated(self):
+        """
+        Checks if credentials are delegated (server mode).
+
+        :return: ``True`` if credentials are delegated, otherwise ``False``
+        """
+        if self._gss_srv_ctxt.delegated_cred is not None:
+            return True
+        return False
+
+    def save_client_creds(self, client_token):
+        """
+        Save the Client token in a file. This is used by the SSH server
+        to store the client credentials if credentials are delegated
+        (server mode).
+
+        :param str client_token: The GSS-API token received form the client
+        :raises:
+            ``NotImplementedError`` -- Credential delegation is currently not
+            supported in server mode
+        """
+        raise NotImplementedError
+
+
+if __version_info__ < (2, 5):
+    # provide the old name for strict backward compatibility
+    _SSH_GSSAPI = _SSH_GSSAPI_OLD
+
+
+class _SSH_GSSAPI_NEW(_SSH_GSSAuth):
+    """
+    Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
+    using the newer, currently maintained gssapi package.
+
+    :see: `.GSSAuth`
+    """
+
+    def __init__(self, auth_method, gss_deleg_creds):
+        """
+        :param str auth_method: The name of the SSH authentication mechanism
+                                (gssapi-with-mic or gss-keyex)
+        :param bool gss_deleg_creds: Delegate client credentials or not
+        """
+        _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
+
+        if self._gss_deleg_creds:
+            self._gss_flags = (
+                gssapi.RequirementFlag.protection_ready,
+                gssapi.RequirementFlag.integrity,
+                gssapi.RequirementFlag.mutual_authentication,
+                gssapi.RequirementFlag.delegate_to_peer,
+            )
+        else:
+            self._gss_flags = (
+                gssapi.RequirementFlag.protection_ready,
+                gssapi.RequirementFlag.integrity,
+                gssapi.RequirementFlag.mutual_authentication,
+            )
+
+    def ssh_init_sec_context(
+        self, target, desired_mech=None, username=None, recv_token=None
+    ):
+        """
+        Initialize a GSS-API context.
+
+        :param str username: The name of the user who attempts to login
+        :param str target: The hostname of the target to connect to
+        :param str desired_mech: The negotiated GSS-API mechanism
+                                 ("pseudo negotiated" mechanism, because we
+                                 support just the krb5 mechanism :-))
+        :param str recv_token: The GSS-API token received from the Server
+        :raises: `.SSHException` -- Is raised if the desired mechanism of the
+                 client is not supported
+        :raises: ``gssapi.exceptions.GSSError`` if there is an error signaled
+                                                by the GSS-API implementation
+        :return: A ``String`` if the GSS-API has returned a token or ``None``
+                 if no token was returned
+        """
+        from pyasn1.codec.der import decoder
+
+        self._username = username
+        self._gss_host = target
+        targ_name = gssapi.Name(
+            "host@" + self._gss_host,
+            name_type=gssapi.NameType.hostbased_service,
+        )
+        if desired_mech is not None:
+            mech, __ = decoder.decode(desired_mech)
+            if mech.__str__() != self._krb5_mech:
+                raise SSHException("Unsupported mechanism OID.")
+        krb5_mech = gssapi.MechType.kerberos
+        token = None
+        if recv_token is None:
+            self._gss_ctxt = gssapi.SecurityContext(
+                name=targ_name,
+                flags=self._gss_flags,
+                mech=krb5_mech,
+                usage="initiate",
+            )
+            token = self._gss_ctxt.step(token)
+        else:
+            token = self._gss_ctxt.step(recv_token)
+        self._gss_ctxt_status = self._gss_ctxt.complete
+        return token
+
+    def ssh_get_mic(self, session_id, gss_kex=False):
+        """
+        Create the MIC token for a SSH2 message.
+
+        :param str session_id: The SSH session ID
+        :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
+        :return: gssapi-with-mic:
+                 Returns the MIC token from GSS-API for the message we created
+                 with ``_ssh_build_mic``.
+                 gssapi-keyex:
+                 Returns the MIC token from GSS-API with the SSH session ID as
+                 message.
+        :rtype: str
+        """
+        self._session_id = session_id
+        if not gss_kex:
+            mic_field = self._ssh_build_mic(
+                self._session_id,
+                self._username,
+                self._service,
+                self._auth_method,
+            )
+            mic_token = self._gss_ctxt.get_signature(mic_field)
+        else:
+            # for key exchange with gssapi-keyex
+            mic_token = self._gss_srv_ctxt.get_signature(self._session_id)
+        return mic_token
+
+    def ssh_accept_sec_context(self, hostname, recv_token, username=None):
+        """
+        Accept a GSS-API context (server mode).
+
+        :param str hostname: The servers hostname
+        :param str username: The name of the user who attempts to login
+        :param str recv_token: The GSS-API Token received from the server,
+                               if it's not the initial call.
+        :return: A ``String`` if the GSS-API has returned a token or ``None``
+                if no token was returned
+        """
+        # hostname and username are not required for GSSAPI, but for SSPI
+        self._gss_host = hostname
+        self._username = username
+        if self._gss_srv_ctxt is None:
+            self._gss_srv_ctxt = gssapi.SecurityContext(usage="accept")
+        token = self._gss_srv_ctxt.step(recv_token)
+        self._gss_srv_ctxt_status = self._gss_srv_ctxt.complete
+        return token
+
+    def ssh_check_mic(self, mic_token, session_id, username=None):
+        """
+        Verify the MIC token for a SSH2 message.
+
+        :param str mic_token: The MIC token received from the client
+        :param str session_id: The SSH session ID
+        :param str username: The name of the user who attempts to login
+        :return: None if the MIC check was successful
+        :raises: ``gssapi.exceptions.GSSError`` -- if the MIC check failed
+        """
+        self._session_id = session_id
+        self._username = username
+        if self._username is not None:
+            # server mode
+            mic_field = self._ssh_build_mic(
+                self._session_id,
+                self._username,
+                self._service,
+                self._auth_method,
+            )
+            self._gss_srv_ctxt.verify_signature(mic_field, mic_token)
+        else:
+            # for key exchange with gssapi-keyex
+            # client mode
+            self._gss_ctxt.verify_signature(self._session_id, mic_token)
+
+    @property
+    def credentials_delegated(self):
+        """
+        Checks if credentials are delegated (server mode).
+
+        :return: ``True`` if credentials are delegated, otherwise ``False``
+        :rtype: bool
+        """
+        if self._gss_srv_ctxt.delegated_creds is not None:
+            return True
+        return False
+
+    def save_client_creds(self, client_token):
+        """
+        Save the Client token in a file. This is used by the SSH server
+        to store the client credentials if credentials are delegated
+        (server mode).
+
+        :param str client_token: The GSS-API token received form the client
+        :raises: ``NotImplementedError`` -- Credential delegation is currently
+                 not supported in server mode
+        """
+        raise NotImplementedError
+
+
+class _SSH_SSPI(_SSH_GSSAuth):
+    """
+    Implementation of the Microsoft SSPI Kerberos Authentication for SSH2.
+
+    :see: `.GSSAuth`
+    """
+
+    def __init__(self, auth_method, gss_deleg_creds):
+        """
+        :param str auth_method: The name of the SSH authentication mechanism
+                                (gssapi-with-mic or gss-keyex)
+        :param bool gss_deleg_creds: Delegate client credentials or not
+        """
+        _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
+
+        if self._gss_deleg_creds:
+            self._gss_flags = (
+                sspicon.ISC_REQ_INTEGRITY
+                | sspicon.ISC_REQ_MUTUAL_AUTH
+                | sspicon.ISC_REQ_DELEGATE
+            )
+        else:
+            self._gss_flags = (
+                sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH
+            )
+
+    def ssh_init_sec_context(
+        self, target, desired_mech=None, username=None, recv_token=None
+    ):
+        """
+        Initialize a SSPI context.
+
+        :param str username: The name of the user who attempts to login
+        :param str target: The FQDN of the target to connect to
+        :param str desired_mech: The negotiated SSPI mechanism
+                                 ("pseudo negotiated" mechanism, because we
+                                 support just the krb5 mechanism :-))
+        :param recv_token: The SSPI token received from the Server
+        :raises:
+            `.SSHException` -- Is raised if the desired mechanism of the client
+            is not supported
+        :return: A ``String`` if the SSPI has returned a token or ``None`` if
+                 no token was returned
+        """
+        from pyasn1.codec.der import decoder
+
+        self._username = username
+        self._gss_host = target
+        error = 0
+        targ_name = "host/" + self._gss_host
+        if desired_mech is not None:
+            mech, __ = decoder.decode(desired_mech)
+            if mech.__str__() != self._krb5_mech:
+                raise SSHException("Unsupported mechanism OID.")
+        try:
+            if recv_token is None:
+                self._gss_ctxt = sspi.ClientAuth(
+                    "Kerberos", scflags=self._gss_flags, targetspn=targ_name
+                )
+            error, token = self._gss_ctxt.authorize(recv_token)
+            token = token[0].Buffer
+        except pywintypes.error as e:
+            e.strerror += ", Target: {}".format(self._gss_host)
+            raise
+
+        if error == 0:
+            """
+            if the status is GSS_COMPLETE (error = 0) the context is fully
+            established an we can set _gss_ctxt_status to True.
+            """
+            self._gss_ctxt_status = True
+            token = None
+            """
+            You won't get another token if the context is fully established,
+            so i set token to None instead of ""
+            """
+        return token
+
+    def ssh_get_mic(self, session_id, gss_kex=False):
+        """
+        Create the MIC token for a SSH2 message.
+
+        :param str session_id: The SSH session ID
+        :param bool gss_kex: Generate the MIC for Key Exchange with SSPI or not
+        :return: gssapi-with-mic:
+                 Returns the MIC token from SSPI for the message we created
+                 with ``_ssh_build_mic``.
+                 gssapi-keyex:
+                 Returns the MIC token from SSPI with the SSH session ID as
+                 message.
+        """
+        self._session_id = session_id
+        if not gss_kex:
+            mic_field = self._ssh_build_mic(
+                self._session_id,
+                self._username,
+                self._service,
+                self._auth_method,
+            )
+            mic_token = self._gss_ctxt.sign(mic_field)
+        else:
+            # for key exchange with gssapi-keyex
+            mic_token = self._gss_srv_ctxt.sign(self._session_id)
+        return mic_token
+
+    def ssh_accept_sec_context(self, hostname, username, recv_token):
+        """
+        Accept a SSPI context (server mode).
+
+        :param str hostname: The servers FQDN
+        :param str username: The name of the user who attempts to login
+        :param str recv_token: The SSPI Token received from the server,
+                               if it's not the initial call.
+        :return: A ``String`` if the SSPI has returned a token or ``None`` if
+                 no token was returned
+        """
+        self._gss_host = hostname
+        self._username = username
+        targ_name = "host/" + self._gss_host
+        self._gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=targ_name)
+        error, token = self._gss_srv_ctxt.authorize(recv_token)
+        token = token[0].Buffer
+        if error == 0:
+            self._gss_srv_ctxt_status = True
+            token = None
+        return token
+
+    def ssh_check_mic(self, mic_token, session_id, username=None):
+        """
+        Verify the MIC token for a SSH2 message.
+
+        :param str mic_token: The MIC token received from the client
+        :param str session_id: The SSH session ID
+        :param str username: The name of the user who attempts to login
+        :return: None if the MIC check was successful
+        :raises: ``sspi.error`` -- if the MIC check failed
+        """
+        self._session_id = session_id
+        self._username = username
+        if username is not None:
+            # server mode
+            mic_field = self._ssh_build_mic(
+                self._session_id,
+                self._username,
+                self._service,
+                self._auth_method,
+            )
+            # Verifies data and its signature.  If verification fails, an
+            # sspi.error will be raised.
+            self._gss_srv_ctxt.verify(mic_field, mic_token)
+        else:
+            # for key exchange with gssapi-keyex
+            # client mode
+            # Verifies data and its signature.  If verification fails, an
+            # sspi.error will be raised.
+            self._gss_ctxt.verify(self._session_id, mic_token)
+
+    @property
+    def credentials_delegated(self):
+        """
+        Checks if credentials are delegated (server mode).
+
+        :return: ``True`` if credentials are delegated, otherwise ``False``
+        """
+        return self._gss_flags & sspicon.ISC_REQ_DELEGATE and (
+            self._gss_srv_ctxt_status or self._gss_flags
+        )
+
+    def save_client_creds(self, client_token):
+        """
+        Save the Client token in a file. This is used by the SSH server
+        to store the client credentails if credentials are delegated
+        (server mode).
+
+        :param str client_token: The SSPI token received form the client
+        :raises:
+            ``NotImplementedError`` -- Credential delegation is currently not
+            supported in server mode
+        """
+        raise NotImplementedError
diff --git a/paramiko/transport.py b/paramiko/transport.py
new file mode 100644
index 0000000..a83a901
--- /dev/null
+++ b/paramiko/transport.py
@@ -0,0 +1,3217 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Core protocol implementation
+"""
+
+import os
+import socket
+import sys
+import threading
+import time
+import weakref
+from hashlib import md5, sha1, sha256, sha512
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes
+
+import paramiko
+from paramiko import util
+from paramiko.auth_handler import AuthHandler
+from paramiko.ssh_gss import GSSAuth
+from paramiko.channel import Channel
+from paramiko.common import (
+    xffffffff,
+    cMSG_CHANNEL_OPEN,
+    cMSG_IGNORE,
+    cMSG_GLOBAL_REQUEST,
+    DEBUG,
+    MSG_KEXINIT,
+    MSG_IGNORE,
+    MSG_DISCONNECT,
+    MSG_DEBUG,
+    ERROR,
+    WARNING,
+    cMSG_UNIMPLEMENTED,
+    INFO,
+    cMSG_KEXINIT,
+    cMSG_NEWKEYS,
+    MSG_NEWKEYS,
+    cMSG_REQUEST_SUCCESS,
+    cMSG_REQUEST_FAILURE,
+    CONNECTION_FAILED_CODE,
+    OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+    OPEN_SUCCEEDED,
+    cMSG_CHANNEL_OPEN_FAILURE,
+    cMSG_CHANNEL_OPEN_SUCCESS,
+    MSG_GLOBAL_REQUEST,
+    MSG_REQUEST_SUCCESS,
+    MSG_REQUEST_FAILURE,
+    MSG_CHANNEL_OPEN_SUCCESS,
+    MSG_CHANNEL_OPEN_FAILURE,
+    MSG_CHANNEL_OPEN,
+    MSG_CHANNEL_SUCCESS,
+    MSG_CHANNEL_FAILURE,
+    MSG_CHANNEL_DATA,
+    MSG_CHANNEL_EXTENDED_DATA,
+    MSG_CHANNEL_WINDOW_ADJUST,
+    MSG_CHANNEL_REQUEST,
+    MSG_CHANNEL_EOF,
+    MSG_CHANNEL_CLOSE,
+    MIN_WINDOW_SIZE,
+    MIN_PACKET_SIZE,
+    MAX_WINDOW_SIZE,
+    DEFAULT_WINDOW_SIZE,
+    DEFAULT_MAX_PACKET_SIZE,
+    HIGHEST_USERAUTH_MESSAGE_ID,
+    MSG_UNIMPLEMENTED,
+    MSG_NAMES,
+    MSG_EXT_INFO,
+    cMSG_EXT_INFO,
+    byte_ord,
+)
+from paramiko.compress import ZlibCompressor, ZlibDecompressor
+from paramiko.dsskey import DSSKey
+from paramiko.ed25519key import Ed25519Key
+from paramiko.kex_curve25519 import KexCurve25519
+from paramiko.kex_gex import KexGex, KexGexSHA256
+from paramiko.kex_group1 import KexGroup1
+from paramiko.kex_group14 import KexGroup14, KexGroup14SHA256
+from paramiko.kex_group16 import KexGroup16SHA512
+from paramiko.kex_ecdh_nist import KexNistp256, KexNistp384, KexNistp521
+from paramiko.kex_gss import KexGSSGex, KexGSSGroup1, KexGSSGroup14
+from paramiko.message import Message
+from paramiko.packet import Packetizer, NeedRekeyException
+from paramiko.primes import ModulusPack
+from paramiko.rsakey import RSAKey
+from paramiko.ecdsakey import ECDSAKey
+from paramiko.server import ServerInterface
+from paramiko.sftp_client import SFTPClient
+from paramiko.ssh_exception import (
+    SSHException,
+    BadAuthenticationType,
+    ChannelException,
+    IncompatiblePeer,
+    ProxyCommandFailure,
+)
+from paramiko.util import (
+    ClosingContextManager,
+    clamp_value,
+    b,
+)
+
+
+# for thread cleanup
+_active_threads = []
+
+
+def _join_lingering_threads():
+    for thr in _active_threads:
+        thr.stop_thread()
+
+
+import atexit
+
+atexit.register(_join_lingering_threads)
+
+
+class Transport(threading.Thread, ClosingContextManager):
+    """
+    An SSH Transport attaches to a stream (usually a socket), negotiates an
+    encrypted session, authenticates, and then creates stream tunnels, called
+    `channels <.Channel>`, across the session.  Multiple channels can be
+    multiplexed across a single session (and often are, in the case of port
+    forwardings).
+
+    Instances of this class may be used as context managers.
+    """
+
+    _ENCRYPT = object()
+    _DECRYPT = object()
+
+    _PROTO_ID = "2.0"
+    _CLIENT_ID = "paramiko_{}".format(paramiko.__version__)
+
+    # These tuples of algorithm identifiers are in preference order; do not
+    # reorder without reason!
+    # NOTE: if you need to modify these, we suggest leveraging the
+    # `disabled_algorithms` constructor argument (also available in SSHClient)
+    # instead of monkeypatching or subclassing.
+    _preferred_ciphers = (
+        "aes128-ctr",
+        "aes192-ctr",
+        "aes256-ctr",
+        "aes128-cbc",
+        "aes192-cbc",
+        "aes256-cbc",
+        "3des-cbc",
+    )
+    _preferred_macs = (
+        "hmac-sha2-256",
+        "hmac-sha2-512",
+        "hmac-sha2-256-etm@openssh.com",
+        "hmac-sha2-512-etm@openssh.com",
+        "hmac-sha1",
+        "hmac-md5",
+        "hmac-sha1-96",
+        "hmac-md5-96",
+    )
+    # ~= HostKeyAlgorithms in OpenSSH land
+    _preferred_keys = (
+        "ssh-ed25519",
+        "ecdsa-sha2-nistp256",
+        "ecdsa-sha2-nistp384",
+        "ecdsa-sha2-nistp521",
+        "rsa-sha2-512",
+        "rsa-sha2-256",
+        "ssh-rsa",
+        "ssh-dss",
+    )
+    # ~= PubKeyAcceptedAlgorithms
+    _preferred_pubkeys = (
+        "ssh-ed25519",
+        "ecdsa-sha2-nistp256",
+        "ecdsa-sha2-nistp384",
+        "ecdsa-sha2-nistp521",
+        "rsa-sha2-512",
+        "rsa-sha2-256",
+        "ssh-rsa",
+        "ssh-dss",
+    )
+    _preferred_kex = (
+        "ecdh-sha2-nistp256",
+        "ecdh-sha2-nistp384",
+        "ecdh-sha2-nistp521",
+        "diffie-hellman-group16-sha512",
+        "diffie-hellman-group-exchange-sha256",
+        "diffie-hellman-group14-sha256",
+        "diffie-hellman-group-exchange-sha1",
+        "diffie-hellman-group14-sha1",
+        "diffie-hellman-group1-sha1",
+    )
+    if KexCurve25519.is_available():
+        _preferred_kex = ("curve25519-sha256@libssh.org",) + _preferred_kex
+    _preferred_gsskex = (
+        "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==",
+        "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==",
+        "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==",
+    )
+    _preferred_compression = ("none",)
+
+    _cipher_info = {
+        "aes128-ctr": {
+            "class": algorithms.AES,
+            "mode": modes.CTR,
+            "block-size": 16,
+            "key-size": 16,
+        },
+        "aes192-ctr": {
+            "class": algorithms.AES,
+            "mode": modes.CTR,
+            "block-size": 16,
+            "key-size": 24,
+        },
+        "aes256-ctr": {
+            "class": algorithms.AES,
+            "mode": modes.CTR,
+            "block-size": 16,
+            "key-size": 32,
+        },
+        "aes128-cbc": {
+            "class": algorithms.AES,
+            "mode": modes.CBC,
+            "block-size": 16,
+            "key-size": 16,
+        },
+        "aes192-cbc": {
+            "class": algorithms.AES,
+            "mode": modes.CBC,
+            "block-size": 16,
+            "key-size": 24,
+        },
+        "aes256-cbc": {
+            "class": algorithms.AES,
+            "mode": modes.CBC,
+            "block-size": 16,
+            "key-size": 32,
+        },
+        "3des-cbc": {
+            "class": algorithms.TripleDES,
+            "mode": modes.CBC,
+            "block-size": 8,
+            "key-size": 24,
+        },
+    }
+
+    _mac_info = {
+        "hmac-sha1": {"class": sha1, "size": 20},
+        "hmac-sha1-96": {"class": sha1, "size": 12},
+        "hmac-sha2-256": {"class": sha256, "size": 32},
+        "hmac-sha2-256-etm@openssh.com": {"class": sha256, "size": 32},
+        "hmac-sha2-512": {"class": sha512, "size": 64},
+        "hmac-sha2-512-etm@openssh.com": {"class": sha512, "size": 64},
+        "hmac-md5": {"class": md5, "size": 16},
+        "hmac-md5-96": {"class": md5, "size": 12},
+    }
+
+    _key_info = {
+        # TODO: at some point we will want to drop this as it's no longer
+        # considered secure due to using SHA-1 for signatures. OpenSSH 8.8 no
+        # longer supports it. Question becomes at what point do we want to
+        # prevent users with older setups from using this?
+        "ssh-rsa": RSAKey,
+        "ssh-rsa-cert-v01@openssh.com": RSAKey,
+        "rsa-sha2-256": RSAKey,
+        "rsa-sha2-256-cert-v01@openssh.com": RSAKey,
+        "rsa-sha2-512": RSAKey,
+        "rsa-sha2-512-cert-v01@openssh.com": RSAKey,
+        "ssh-dss": DSSKey,
+        "ssh-dss-cert-v01@openssh.com": DSSKey,
+        "ecdsa-sha2-nistp256": ECDSAKey,
+        "ecdsa-sha2-nistp256-cert-v01@openssh.com": ECDSAKey,
+        "ecdsa-sha2-nistp384": ECDSAKey,
+        "ecdsa-sha2-nistp384-cert-v01@openssh.com": ECDSAKey,
+        "ecdsa-sha2-nistp521": ECDSAKey,
+        "ecdsa-sha2-nistp521-cert-v01@openssh.com": ECDSAKey,
+        "ssh-ed25519": Ed25519Key,
+        "ssh-ed25519-cert-v01@openssh.com": Ed25519Key,
+    }
+
+    _kex_info = {
+        "diffie-hellman-group1-sha1": KexGroup1,
+        "diffie-hellman-group14-sha1": KexGroup14,
+        "diffie-hellman-group-exchange-sha1": KexGex,
+        "diffie-hellman-group-exchange-sha256": KexGexSHA256,
+        "diffie-hellman-group14-sha256": KexGroup14SHA256,
+        "diffie-hellman-group16-sha512": KexGroup16SHA512,
+        "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGroup1,
+        "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGroup14,
+        "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGex,
+        "ecdh-sha2-nistp256": KexNistp256,
+        "ecdh-sha2-nistp384": KexNistp384,
+        "ecdh-sha2-nistp521": KexNistp521,
+    }
+    if KexCurve25519.is_available():
+        _kex_info["curve25519-sha256@libssh.org"] = KexCurve25519
+
+    _compression_info = {
+        # zlib@openssh.com is just zlib, but only turned on after a successful
+        # authentication.  openssh servers may only offer this type because
+        # they've had troubles with security holes in zlib in the past.
+        "zlib@openssh.com": (ZlibCompressor, ZlibDecompressor),
+        "zlib": (ZlibCompressor, ZlibDecompressor),
+        "none": (None, None),
+    }
+
+    _modulus_pack = None
+    _active_check_timeout = 0.1
+
+    def __init__(
+        self,
+        sock,
+        default_window_size=DEFAULT_WINDOW_SIZE,
+        default_max_packet_size=DEFAULT_MAX_PACKET_SIZE,
+        gss_kex=False,
+        gss_deleg_creds=True,
+        disabled_algorithms=None,
+        server_sig_algs=True,
+    ):
+        """
+        Create a new SSH session over an existing socket, or socket-like
+        object.  This only creates the `.Transport` object; it doesn't begin
+        the SSH session yet.  Use `connect` or `start_client` to begin a client
+        session, or `start_server` to begin a server session.
+
+        If the object is not actually a socket, it must have the following
+        methods:
+
+        - ``send(bytes)``: Writes from 1 to ``len(bytes)`` bytes, and returns
+          an int representing the number of bytes written.  Returns
+          0 or raises ``EOFError`` if the stream has been closed.
+        - ``recv(int)``: Reads from 1 to ``int`` bytes and returns them as a
+          string.  Returns 0 or raises ``EOFError`` if the stream has been
+          closed.
+        - ``close()``: Closes the socket.
+        - ``settimeout(n)``: Sets a (float) timeout on I/O operations.
+
+        For ease of use, you may also pass in an address (as a tuple) or a host
+        string as the ``sock`` argument.  (A host string is a hostname with an
+        optional port (separated by ``":"``) which will be converted into a
+        tuple of ``(hostname, port)``.)  A socket will be connected to this
+        address and used for communication.  Exceptions from the ``socket``
+        call may be thrown in this case.
+
+        .. note::
+            Modifying the the window and packet sizes might have adverse
+            effects on your channels created from this transport. The default
+            values are the same as in the OpenSSH code base and have been
+            battle tested.
+
+        :param socket sock:
+            a socket or socket-like object to create the session over.
+        :param int default_window_size:
+            sets the default window size on the transport. (defaults to
+            2097152)
+        :param int default_max_packet_size:
+            sets the default max packet size on the transport. (defaults to
+            32768)
+        :param bool gss_kex:
+            Whether to enable GSSAPI key exchange when GSSAPI is in play.
+            Default: ``False``.
+        :param bool gss_deleg_creds:
+            Whether to enable GSSAPI credential delegation when GSSAPI is in
+            play. Default: ``True``.
+        :param dict disabled_algorithms:
+            If given, must be a dictionary mapping algorithm type to an
+            iterable of algorithm identifiers, which will be disabled for the
+            lifetime of the transport.
+
+            Keys should match the last word in the class' builtin algorithm
+            tuple attributes, such as ``"ciphers"`` to disable names within
+            ``_preferred_ciphers``; or ``"kex"`` to disable something defined
+            inside ``_preferred_kex``. Values should exactly match members of
+            the matching attribute.
+
+            For example, if you need to disable
+            ``diffie-hellman-group16-sha512`` key exchange (perhaps because
+            your code talks to a server which implements it differently from
+            Paramiko), specify ``disabled_algorithms={"kex":
+            ["diffie-hellman-group16-sha512"]}``.
+        :param bool server_sig_algs:
+            Whether to send an extra message to compatible clients, in server
+            mode, with a list of supported pubkey algorithms. Default:
+            ``True``.
+
+        .. versionchanged:: 1.15
+            Added the ``default_window_size`` and ``default_max_packet_size``
+            arguments.
+        .. versionchanged:: 1.15
+            Added the ``gss_kex`` and ``gss_deleg_creds`` kwargs.
+        .. versionchanged:: 2.6
+            Added the ``disabled_algorithms`` kwarg.
+        .. versionchanged:: 2.9
+            Added the ``server_sig_algs`` kwarg.
+        """
+        self.active = False
+        self.hostname = None
+        self.server_extensions = {}
+
+        if isinstance(sock, str):
+            
+            # convert "host:port" into (host, port)
+            hl = sock.split(":", 1)
+            self.hostname = hl[0]
+            if len(hl) == 1:
+                sock = (hl[0], 22)
+            else:
+                sock = (hl[0], int(hl[1]))
+        if type(sock) is tuple:
+            # connect to the given (host, port)
+            hostname, port = sock
+            self.hostname = hostname
+            reason = "No suitable address family"
+            addrinfos = socket.getaddrinfo(
+                hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
+            )
+            for family, socktype, proto, canonname, sockaddr in addrinfos:
+                if socktype == socket.SOCK_STREAM:
+                    af = family
+                    # addr = sockaddr
+                    sock = socket.socket(af, socket.SOCK_STREAM)
+                    try:
+                        sock.connect((hostname, port))
+                    except socket.error as e:
+                        reason = str(e)
+                    else:
+                        break
+            else:
+                raise SSHException(
+                    "Unable to connect to {}: {}".format(hostname, reason)
+                )
+        # okay, normal socket-ish flow here...
+        threading.Thread.__init__(self)
+        self.daemon = True
+        self.sock = sock
+        # we set the timeout so we can check self.active periodically to
+        # see if we should bail. socket.timeout exception is never propagated.
+        self.sock.settimeout(self._active_check_timeout)
+
+        # negotiated crypto parameters
+        self.packetizer = Packetizer(sock)
+        self.local_version = "SSH-" + self._PROTO_ID + "-" + self._CLIENT_ID
+        self.remote_version = ""
+        self.local_cipher = self.remote_cipher = ""
+        self.local_kex_init = self.remote_kex_init = None
+        self.local_mac = self.remote_mac = None
+        self.local_compression = self.remote_compression = None
+        self.session_id = None
+        self.host_key_type = None
+        self.host_key = None
+
+        # GSS-API / SSPI Key Exchange
+        self.use_gss_kex = gss_kex
+        # This will be set to True if GSS-API Key Exchange was performed
+        self.gss_kex_used = False
+        self.kexgss_ctxt = None
+        self.gss_host = None
+        if self.use_gss_kex:
+            self.kexgss_ctxt = GSSAuth("gssapi-keyex", gss_deleg_creds)
+            self._preferred_kex = self._preferred_gsskex + self._preferred_kex
+
+        # state used during negotiation
+        self.kex_engine = None
+        self.H = None
+        self.K = None
+
+        self.initial_kex_done = False
+        self.in_kex = False
+        self.authenticated = False
+        self._expected_packet = tuple()
+        # synchronization (always higher level than write_lock)
+        self.lock = threading.Lock()
+
+        # tracking open channels
+        self._channels = ChannelMap()
+        self.channel_events = {}  # (id -> Event)
+        self.channels_seen = {}  # (id -> True)
+        self._channel_counter = 0
+        self.default_max_packet_size = default_max_packet_size
+        self.default_window_size = default_window_size
+        self._forward_agent_handler = None
+        self._x11_handler = None
+        self._tcp_handler = None
+
+        self.saved_exception = None
+        self.clear_to_send = threading.Event()
+        self.clear_to_send_lock = threading.Lock()
+        self.clear_to_send_timeout = 30.0
+        self.log_name = "paramiko.transport"
+        self.logger = util.get_logger(self.log_name)
+        self.packetizer.set_log(self.logger)
+        self.auth_handler = None
+        # response Message from an arbitrary global request
+        self.global_response = None
+        # user-defined event callbacks
+        self.completion_event = None
+        # how long (seconds) to wait for the SSH banner
+        self.banner_timeout = 15
+        # how long (seconds) to wait for the handshake to finish after SSH
+        # banner sent.
+        self.handshake_timeout = 15
+        # how long (seconds) to wait for the auth response.
+        self.auth_timeout = 30
+        # how long (seconds) to wait for opening a channel
+        self.channel_timeout = 60 * 60
+        self.disabled_algorithms = disabled_algorithms or {}
+        self.server_sig_algs = server_sig_algs
+
+        # server mode:
+        self.server_mode = False
+        self.server_object = None
+        self.server_key_dict = {}
+        self.server_accepts = []
+        self.server_accept_cv = threading.Condition(self.lock)
+        self.subsystem_table = {}
+
+    def _filter_algorithm(self, type_):
+        default = getattr(self, "_preferred_{}".format(type_))
+        return tuple(
+            x
+            for x in default
+            if x not in self.disabled_algorithms.get(type_, [])
+        )
+
+    @property
+    def preferred_ciphers(self):
+        return self._filter_algorithm("ciphers")
+
+    @property
+    def preferred_macs(self):
+        return self._filter_algorithm("macs")
+
+    @property
+    def preferred_keys(self):
+        # Interleave cert variants here; resistant to various background
+        # overwriting of _preferred_keys, and necessary as hostkeys can't use
+        # the logic pubkey auth does re: injecting/checking for certs at
+        # runtime
+        filtered = self._filter_algorithm("keys")
+        return tuple(
+            filtered
+            + tuple("{}-cert-v01@openssh.com".format(x) for x in filtered)
+        )
+
+    @property
+    def preferred_pubkeys(self):
+        return self._filter_algorithm("pubkeys")
+
+    @property
+    def preferred_kex(self):
+        return self._filter_algorithm("kex")
+
+    @property
+    def preferred_compression(self):
+        return self._filter_algorithm("compression")
+
+    def __repr__(self):
+        """
+        Returns a string representation of this object, for debugging.
+        """
+        id_ = hex(id(self) & xffffffff)
+        out = "<paramiko.Transport at {}".format(id_)
+        if not self.active:
+            out += " (unconnected)"
+        else:
+            if self.local_cipher != "":
+                out += " (cipher {}, {:d} bits)".format(
+                    self.local_cipher,
+                    self._cipher_info[self.local_cipher]["key-size"] * 8,
+                )
+            if self.is_authenticated():
+                out += " (active; {} open channel(s))".format(
+                    len(self._channels)
+                )
+            elif self.initial_kex_done:
+                out += " (connected; awaiting auth)"
+            else:
+                out += " (connecting)"
+        out += ">"
+        return out
+
+    def atfork(self):
+        """
+        Terminate this Transport without closing the session.  On posix
+        systems, if a Transport is open during process forking, both parent
+        and child will share the underlying socket, but only one process can
+        use the connection (without corrupting the session).  Use this method
+        to clean up a Transport object without disrupting the other process.
+
+        .. versionadded:: 1.5.3
+        """
+        self.sock.close()
+        self.close()
+
+    def get_security_options(self):
+        print("get_security_options")
+        """
+        Return a `.SecurityOptions` object which can be used to tweak the
+        encryption algorithms this transport will permit (for encryption,
+        digest/hash operations, public keys, and key exchanges) and the order
+        of preference for them.
+        """
+        return SecurityOptions(self)
+
+    def set_gss_host(self, gss_host, trust_dns=True, gssapi_requested=True):
+        print("set_gss_host")
+        """
+        Normalize/canonicalize ``self.gss_host`` depending on various factors.
+
+        :param str gss_host:
+            The explicitly requested GSS-oriented hostname to connect to (i.e.
+            what the host's name is in the Kerberos database.) Defaults to
+            ``self.hostname`` (which will be the 'real' target hostname and/or
+            host portion of given socket object.)
+        :param bool trust_dns:
+            Indicates whether or not DNS is trusted; if true, DNS will be used
+            to canonicalize the GSS hostname (which again will either be
+            ``gss_host`` or the transport's default hostname.)
+            (Defaults to True due to backwards compatibility.)
+        :param bool gssapi_requested:
+            Whether GSSAPI key exchange or authentication was even requested.
+            If not, this is a no-op and nothing happens
+            (and ``self.gss_host`` is not set.)
+            (Defaults to True due to backwards compatibility.)
+        :returns: ``None``.
+        """
+        # No GSSAPI in play == nothing to do
+        if not gssapi_requested:
+            return
+        # Obtain the correct host first - did user request a GSS-specific name
+        # to use that is distinct from the actual SSH target hostname?
+        if gss_host is None:
+            gss_host = self.hostname
+        # Finally, canonicalize via DNS if DNS is trusted.
+        if trust_dns and gss_host is not None:
+            gss_host = socket.getfqdn(gss_host)
+        # And set attribute for reference later.
+        self.gss_host = gss_host
+
+    def start_client(self, event=None, timeout=None):
+        print("start_client")
+        """
+        Negotiate a new SSH2 session as a client.  This is the first step after
+        creating a new `.Transport`.  A separate thread is created for protocol
+        negotiation.
+
+        If an event is passed in, this method returns immediately.  When
+        negotiation is done (successful or not), the given ``Event`` will
+        be triggered.  On failure, `is_active` will return ``False``.
+
+        (Since 1.4) If ``event`` is ``None``, this method will not return until
+        negotiation is done.  On success, the method returns normally.
+        Otherwise an SSHException is raised.
+
+        After a successful negotiation, you will usually want to authenticate,
+        calling `auth_password <Transport.auth_password>` or
+        `auth_publickey <Transport.auth_publickey>`.
+
+        .. note:: `connect` is a simpler method for connecting as a client.
+
+        .. note::
+            After calling this method (or `start_server` or `connect`), you
+            should no longer directly read from or write to the original socket
+            object.
+
+        :param .threading.Event event:
+            an event to trigger when negotiation is complete (optional)
+
+        :param float timeout:
+            a timeout, in seconds, for SSH2 session negotiation (optional)
+
+        :raises:
+            `.SSHException` -- if negotiation fails (and no ``event`` was
+            passed in)
+        """
+        self.active = True
+        if event is not None:
+            # async, return immediately and let the app poll for completion
+            self.completion_event = event
+            self.start()
+            return
+
+        # synchronous, wait for a result
+        self.completion_event = event = threading.Event()
+        self.start()
+        max_time = time.time() + timeout if timeout is not None else None
+        while True:
+            event.wait(0.1)
+            if not self.active:
+                e = self.get_exception()
+                if e is not None:
+                    raise e
+                raise SSHException("Negotiation failed.")
+            if event.is_set() or (
+                timeout is not None and time.time() >= max_time
+            ):
+                break
+
+    def start_server(self, event=None, server=None):
+        print("start_server")
+        """
+        Negotiate a new SSH2 session as a server.  This is the first step after
+        creating a new `.Transport` and setting up your server host key(s).  A
+        separate thread is created for protocol negotiation.
+
+        If an event is passed in, this method returns immediately.  When
+        negotiation is done (successful or not), the given ``Event`` will
+        be triggered.  On failure, `is_active` will return ``False``.
+
+        (Since 1.4) If ``event`` is ``None``, this method will not return until
+        negotiation is done.  On success, the method returns normally.
+        Otherwise an SSHException is raised.
+
+        After a successful negotiation, the client will need to authenticate.
+        Override the methods `get_allowed_auths
+        <.ServerInterface.get_allowed_auths>`, `check_auth_none
+        <.ServerInterface.check_auth_none>`, `check_auth_password
+        <.ServerInterface.check_auth_password>`, and `check_auth_publickey
+        <.ServerInterface.check_auth_publickey>` in the given ``server`` object
+        to control the authentication process.
+
+        After a successful authentication, the client should request to open a
+        channel.  Override `check_channel_request
+        <.ServerInterface.check_channel_request>` in the given ``server``
+        object to allow channels to be opened.
+
+        .. note::
+            After calling this method (or `start_client` or `connect`), you
+            should no longer directly read from or write to the original socket
+            object.
+
+        :param .threading.Event event:
+            an event to trigger when negotiation is complete.
+        :param .ServerInterface server:
+            an object used to perform authentication and create `channels
+            <.Channel>`
+
+        :raises:
+            `.SSHException` -- if negotiation fails (and no ``event`` was
+            passed in)
+        """
+        if server is None:
+            server = ServerInterface()
+        self.server_mode = True
+        self.server_object = server
+        self.active = True
+        if event is not None:
+            # async, return immediately and let the app poll for completion
+            self.completion_event = event
+            self.start()
+            return
+
+        # synchronous, wait for a result
+        self.completion_event = event = threading.Event()
+        self.start()
+        while True:
+            event.wait(0.1)
+            if not self.active:
+                e = self.get_exception()
+                if e is not None:
+                    raise e
+                raise SSHException("Negotiation failed.")
+            if event.is_set():
+                break
+
+    def add_server_key(self, key):
+        print("add_server_key")
+        """
+        Add a host key to the list of keys used for server mode.  When behaving
+        as a server, the host key is used to sign certain packets during the
+        SSH2 negotiation, so that the client can trust that we are who we say
+        we are.  Because this is used for signing, the key must contain private
+        key info, not just the public half.  Only one key of each type (RSA or
+        DSS) is kept.
+
+        :param .PKey key:
+            the host key to add, usually an `.RSAKey` or `.DSSKey`.
+        """
+        self.server_key_dict[key.get_name()] = key
+        # Handle SHA-2 extensions for RSA by ensuring that lookups into
+        # self.server_key_dict will yield this key for any of the algorithm
+        # names.
+        if isinstance(key, RSAKey):
+            self.server_key_dict["rsa-sha2-256"] = key
+            self.server_key_dict["rsa-sha2-512"] = key
+
+    def get_server_key(self):
+        print("get_server_key")
+        """
+        Return the active host key, in server mode.  After negotiating with the
+        client, this method will return the negotiated host key.  If only one
+        type of host key was set with `add_server_key`, that's the only key
+        that will ever be returned.  But in cases where you have set more than
+        one type of host key (for example, an RSA key and a DSS key), the key
+        type will be negotiated by the client, and this method will return the
+        key of the type agreed on.  If the host key has not been negotiated
+        yet, ``None`` is returned.  In client mode, the behavior is undefined.
+
+        :return:
+            host key (`.PKey`) of the type negotiated by the client, or
+            ``None``.
+        """
+        try:
+            return self.server_key_dict[self.host_key_type]
+        except KeyError:
+            pass
+        return None
+
+    @staticmethod
+    def load_server_moduli(filename=None):
+        print("load_server_moduli")
+        """
+        (optional)
+        Load a file of prime moduli for use in doing group-exchange key
+        negotiation in server mode.  It's a rather obscure option and can be
+        safely ignored.
+
+        In server mode, the remote client may request "group-exchange" key
+        negotiation, which asks the server to send a random prime number that
+        fits certain criteria.  These primes are pretty difficult to compute,
+        so they can't be generated on demand.  But many systems contain a file
+        of suitable primes (usually named something like ``/etc/ssh/moduli``).
+        If you call `load_server_moduli` and it returns ``True``, then this
+        file of primes has been loaded and we will support "group-exchange" in
+        server mode.  Otherwise server mode will just claim that it doesn't
+        support that method of key negotiation.
+
+        :param str filename:
+            optional path to the moduli file, if you happen to know that it's
+            not in a standard location.
+        :return:
+            True if a moduli file was successfully loaded; False otherwise.
+
+        .. note:: This has no effect when used in client mode.
+        """
+        Transport._modulus_pack = ModulusPack()
+        # places to look for the openssh "moduli" file
+        file_list = ["/etc/ssh/moduli", "/usr/local/etc/moduli"]
+        if filename is not None:
+            file_list.insert(0, filename)
+        for fn in file_list:
+            try:
+                Transport._modulus_pack.read_file(fn)
+                return True
+            except IOError:
+                pass
+        # none succeeded
+        Transport._modulus_pack = None
+        return False
+
+    def close(self):
+        print("close")
+        """
+        Close this session, and any open channels that are tied to it.
+        """
+        if not self.active:
+            return
+        self.stop_thread()
+        for chan in list(self._channels.values()):
+            chan._unlink()
+        self.sock.close()
+
+    def get_remote_server_key(self):
+        print("get_remote_server_key")
+        """
+        Return the host key of the server (in client mode).
+
+        .. note::
+            Previously this call returned a tuple of ``(key type, key
+            string)``. You can get the same effect by calling `.PKey.get_name`
+            for the key type, and ``str(key)`` for the key string.
+
+        :raises: `.SSHException` -- if no session is currently active.
+
+        :return: public key (`.PKey`) of the remote server
+        """
+        if (not self.active) or (not self.initial_kex_done):
+            raise SSHException("No existing session")
+        return self.host_key
+
+    def is_active(self):
+        print("is_active")
+        """
+        Return true if this session is active (open).
+
+        :return:
+            True if the session is still active (open); False if the session is
+            closed
+        """
+        return self.active
+
+    def open_session(
+        self, window_size=None, max_packet_size=None, timeout=None
+    ):
+        """
+        Request a new channel to the server, of type ``"session"``.  This is
+        just an alias for calling `open_channel` with an argument of
+        ``"session"``.
+
+        .. note:: Modifying the the window and packet sizes might have adverse
+            effects on the session created. The default values are the same
+            as in the OpenSSH code base and have been battle tested.
+
+        :param int window_size:
+            optional window size for this session.
+        :param int max_packet_size:
+            optional max packet size for this session.
+
+        :return: a new `.Channel`
+
+        :raises:
+            `.SSHException` -- if the request is rejected or the session ends
+            prematurely
+
+        .. versionchanged:: 1.13.4/1.14.3/1.15.3
+            Added the ``timeout`` argument.
+        .. versionchanged:: 1.15
+            Added the ``window_size`` and ``max_packet_size`` arguments.
+        """
+        return self.open_channel(
+            "session",
+            window_size=window_size,
+            max_packet_size=max_packet_size,
+            timeout=timeout,
+        )
+
+    def open_x11_channel(self, src_addr=None):
+        print("open_x11_channel")
+        """
+        Request a new channel to the client, of type ``"x11"``.  This
+        is just an alias for ``open_channel('x11', src_addr=src_addr)``.
+
+        :param tuple src_addr:
+            the source address (``(str, int)``) of the x11 server (port is the
+            x11 port, ie. 6010)
+        :return: a new `.Channel`
+
+        :raises:
+            `.SSHException` -- if the request is rejected or the session ends
+            prematurely
+        """
+        return self.open_channel("x11", src_addr=src_addr)
+
+    def open_forward_agent_channel(self):
+        print("open_forward_agent_channel")
+        """
+        Request a new channel to the client, of type
+        ``"auth-agent@openssh.com"``.
+
+        This is just an alias for ``open_channel('auth-agent@openssh.com')``.
+
+        :return: a new `.Channel`
+
+        :raises: `.SSHException` --
+            if the request is rejected or the session ends prematurely
+        """
+        return self.open_channel("auth-agent@openssh.com")
+
+    def open_forwarded_tcpip_channel(self, src_addr, dest_addr):
+        print("open_forwarded_tcpip_channel")
+        """
+        Request a new channel back to the client, of type ``forwarded-tcpip``.
+
+        This is used after a client has requested port forwarding, for sending
+        incoming connections back to the client.
+
+        :param src_addr: originator's address
+        :param dest_addr: local (server) connected address
+        """
+        return self.open_channel("forwarded-tcpip", dest_addr, src_addr)
+
+    def open_channel(
+        self,
+        kind,
+        dest_addr=None,
+        src_addr=None,
+        window_size=None,
+        max_packet_size=None,
+        timeout=None,
+    ):
+        """
+        Request a new channel to the server. `Channels <.Channel>` are
+        socket-like objects used for the actual transfer of data across the
+        session. You may only request a channel after negotiating encryption
+        (using `connect` or `start_client`) and authenticating.
+
+        .. note:: Modifying the the window and packet sizes might have adverse
+            effects on the channel created. The default values are the same
+            as in the OpenSSH code base and have been battle tested.
+
+        :param str kind:
+            the kind of channel requested (usually ``"session"``,
+            ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``)
+        :param tuple dest_addr:
+            the destination address (address + port tuple) of this port
+            forwarding, if ``kind`` is ``"forwarded-tcpip"`` or
+            ``"direct-tcpip"`` (ignored for other channel types)
+        :param src_addr: the source address of this port forwarding, if
+            ``kind`` is ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``
+        :param int window_size:
+            optional window size for this session.
+        :param int max_packet_size:
+            optional max packet size for this session.
+        :param float timeout:
+            optional timeout opening a channel, default 3600s (1h)
+
+        :return: a new `.Channel` on success
+
+        :raises:
+            `.SSHException` -- if the request is rejected, the session ends
+            prematurely or there is a timeout opening a channel
+
+        .. versionchanged:: 1.15
+            Added the ``window_size`` and ``max_packet_size`` arguments.
+        """
+        if not self.active:
+            raise SSHException("SSH session not active")
+        timeout = self.channel_timeout if timeout is None else timeout
+        self.lock.acquire()
+        try:
+            window_size = self._sanitize_window_size(window_size)
+            max_packet_size = self._sanitize_packet_size(max_packet_size)
+            chanid = self._next_channel()
+            m = Message()
+            m.add_byte(cMSG_CHANNEL_OPEN)
+            m.add_string(kind)
+            m.add_int(chanid)
+            m.add_int(window_size)
+            m.add_int(max_packet_size)
+            if (kind == "forwarded-tcpip") or (kind == "direct-tcpip"):
+                m.add_string(dest_addr[0])
+                m.add_int(dest_addr[1])
+                m.add_string(src_addr[0])
+                m.add_int(src_addr[1])
+            elif kind == "x11":
+                m.add_string(src_addr[0])
+                m.add_int(src_addr[1])
+            chan = Channel(chanid)
+            self._channels.put(chanid, chan)
+            self.channel_events[chanid] = event = threading.Event()
+            self.channels_seen[chanid] = True
+            chan._set_transport(self)
+            chan._set_window(window_size, max_packet_size)
+        finally:
+            self.lock.release()
+        self._send_user_message(m)
+        start_ts = time.time()
+        while True:
+            event.wait(0.1)
+            if not self.active:
+                e = self.get_exception()
+                if e is None:
+                    e = SSHException("Unable to open channel.")
+                raise e
+            if event.is_set():
+                break
+            elif start_ts + timeout < time.time():
+                raise SSHException("Timeout opening channel.")
+        chan = self._channels.get(chanid)
+        if chan is not None:
+            return chan
+        e = self.get_exception()
+        if e is None:
+            e = SSHException("Unable to open channel.")
+        raise e
+
+    def request_port_forward(self, address, port, handler=None):
+        print("request_port_forward")
+        """
+        Ask the server to forward TCP connections from a listening port on
+        the server, across this SSH session.
+
+        If a handler is given, that handler is called from a different thread
+        whenever a forwarded connection arrives.  The handler parameters are::
+
+            handler(
+                channel,
+                (origin_addr, origin_port),
+                (server_addr, server_port),
+            )
+
+        where ``server_addr`` and ``server_port`` are the address and port that
+        the server was listening on.
+
+        If no handler is set, the default behavior is to send new incoming
+        forwarded connections into the accept queue, to be picked up via
+        `accept`.
+
+        :param str address: the address to bind when forwarding
+        :param int port:
+            the port to forward, or 0 to ask the server to allocate any port
+        :param callable handler:
+            optional handler for incoming forwarded connections, of the form
+            ``func(Channel, (str, int), (str, int))``.
+
+        :return: the port number (`int`) allocated by the server
+
+        :raises:
+            `.SSHException` -- if the server refused the TCP forward request
+        """
+        if not self.active:
+            raise SSHException("SSH session not active")
+        port = int(port)
+        response = self.global_request(
+            "tcpip-forward", (address, port), wait=True
+        )
+        if response is None:
+            raise SSHException("TCP forwarding request denied")
+        if port == 0:
+            port = response.get_int()
+        if handler is None:
+
+            def default_handler(channel, src_addr, dest_addr_port):
+                # src_addr, src_port = src_addr_port
+                # dest_addr, dest_port = dest_addr_port
+                self._queue_incoming_channel(channel)
+
+            handler = default_handler
+        self._tcp_handler = handler
+        return port
+
+    def cancel_port_forward(self, address, port):
+        print("cancel_port_forward")
+        """
+        Ask the server to cancel a previous port-forwarding request.  No more
+        connections to the given address & port will be forwarded across this
+        ssh connection.
+
+        :param str address: the address to stop forwarding
+        :param int port: the port to stop forwarding
+        """
+        if not self.active:
+            return
+        self._tcp_handler = None
+        self.global_request("cancel-tcpip-forward", (address, port), wait=True)
+
+    def open_sftp_client(self):
+        print("open_sftp_client")
+        """
+        Create an SFTP client channel from an open transport.  On success, an
+        SFTP session will be opened with the remote host, and a new
+        `.SFTPClient` object will be returned.
+
+        :return:
+            a new `.SFTPClient` referring to an sftp session (channel) across
+            this transport
+        """
+        return SFTPClient.from_transport(self)
+
+    def send_ignore(self, byte_count=None):
+        print("send_ignore")
+        """
+        Send a junk packet across the encrypted link.  This is sometimes used
+        to add "noise" to a connection to confuse would-be attackers.  It can
+        also be used as a keep-alive for long lived connections traversing
+        firewalls.
+
+        :param int byte_count:
+            the number of random bytes to send in the payload of the ignored
+            packet -- defaults to a random number from 10 to 41.
+        """
+        m = Message()
+        m.add_byte(cMSG_IGNORE)
+        if byte_count is None:
+            byte_count = (byte_ord(os.urandom(1)) % 32) + 10
+        m.add_bytes(os.urandom(byte_count))
+        self._send_user_message(m)
+
+    def renegotiate_keys(self):
+        print("renegotiate_keys")
+        """
+        Force this session to switch to new keys.  Normally this is done
+        automatically after the session hits a certain number of packets or
+        bytes sent or received, but this method gives you the option of forcing
+        new keys whenever you want.  Negotiating new keys causes a pause in
+        traffic both ways as the two sides swap keys and do computations.  This
+        method returns when the session has switched to new keys.
+
+        :raises:
+            `.SSHException` -- if the key renegotiation failed (which causes
+            the session to end)
+        """
+        self.completion_event = threading.Event()
+        self._send_kex_init()
+        while True:
+            self.completion_event.wait(0.1)
+            if not self.active:
+                e = self.get_exception()
+                if e is not None:
+                    raise e
+                raise SSHException("Negotiation failed.")
+            if self.completion_event.is_set():
+                break
+        return
+
+    def set_keepalive(self, interval):
+        print("set_keepalive")
+        """
+        Turn on/off keepalive packets (default is off).  If this is set, after
+        ``interval`` seconds without sending any data over the connection, a
+        "keepalive" packet will be sent (and ignored by the remote host).  This
+        can be useful to keep connections alive over a NAT, for example.
+
+        :param int interval:
+            seconds to wait before sending a keepalive packet (or
+            0 to disable keepalives).
+        """
+
+        def _request(x=weakref.proxy(self)):
+            return x.global_request("keepalive@lag.net", wait=False)
+
+        self.packetizer.set_keepalive(interval, _request)
+
+    def global_request(self, kind, data=None, wait=True):
+
+        print("global_request")
+        """
+        Make a global request to the remote host.  These are normally
+        extensions to the SSH2 protocol.
+
+        :param str kind: name of the request.
+        :param tuple data:
+            an optional tuple containing additional data to attach to the
+            request.
+        :param bool wait:
+            ``True`` if this method should not return until a response is
+            received; ``False`` otherwise.
+        :return:
+            a `.Message` containing possible additional data if the request was
+            successful (or an empty `.Message` if ``wait`` was ``False``);
+            ``None`` if the request was denied.
+        """
+        if wait:
+            self.completion_event = threading.Event()
+        m = Message()
+        m.add_byte(cMSG_GLOBAL_REQUEST)
+        m.add_string(kind)
+        m.add_boolean(wait)
+        if data is not None:
+            m.add(*data)
+        self._log(DEBUG, 'Sending global request "{}"'.format(kind))
+        self._send_user_message(m)
+        if not wait:
+            return None
+        while True:
+            self.completion_event.wait(0.1)
+            if not self.active:
+                return None
+            if self.completion_event.is_set():
+                break
+        return self.global_response
+
+    def accept(self, timeout=None):
+        print("accept")
+        """
+        Return the next channel opened by the client over this transport, in
+        server mode.  If no channel is opened before the given timeout,
+        ``None`` is returned.
+
+        :param int timeout:
+            seconds to wait for a channel, or ``None`` to wait forever
+        :return: a new `.Channel` opened by the client
+        """
+
+
+        self.lock.acquire()
+        try:
+            if len(self.server_accepts) > 0:
+                chan = self.server_accepts.pop(0)
+            else:
+                self.server_accept_cv.wait(timeout)
+                if len(self.server_accepts) > 0:
+                    chan = self.server_accepts.pop(0)
+                else:
+                    # timeout
+                    chan = None
+        finally:
+            self.lock.release()
+        return chan
+
+    def connect(
+        self,
+        hostkey=None,
+        username="",
+        password=None,
+        pkey=None,
+        gss_host=None,
+        gss_auth=False,
+        gss_kex=False,
+        gss_deleg_creds=True,
+        gss_trust_dns=True,
+    ):
+        print("Connect")
+        """
+        Negotiate an SSH2 session, and optionally verify the server's host key
+        and authenticate using a password or private key.  This is a shortcut
+        for `start_client`, `get_remote_server_key`, and
+        `Transport.auth_password` or `Transport.auth_publickey`.  Use those
+        methods if you want more control.
+
+        You can use this method immediately after creating a Transport to
+        negotiate encryption with a server.  If it fails, an exception will be
+        thrown.  On success, the method will return cleanly, and an encrypted
+        session exists.  You may immediately call `open_channel` or
+        `open_session` to get a `.Channel` object, which is used for data
+        transfer.
+
+        .. note::
+            If you fail to supply a password or private key, this method may
+            succeed, but a subsequent `open_channel` or `open_session` call may
+            fail because you haven't authenticated yet.
+
+        :param .PKey hostkey:
+            the host key expected from the server, or ``None`` if you don't
+            want to do host key verification.
+        :param str username: the username to authenticate as.
+        :param str password:
+            a password to use for authentication, if you want to use password
+            authentication; otherwise ``None``.
+        :param .PKey pkey:
+            a private key to use for authentication, if you want to use private
+            key authentication; otherwise ``None``.
+        :param str gss_host:
+            The target's name in the kerberos database. Default: hostname
+        :param bool gss_auth:
+            ``True`` if you want to use GSS-API authentication.
+        :param bool gss_kex:
+            Perform GSS-API Key Exchange and user authentication.
+        :param bool gss_deleg_creds:
+            Whether to delegate GSS-API client credentials.
+        :param gss_trust_dns:
+            Indicates whether or not the DNS is trusted to securely
+            canonicalize the name of the host being connected to (default
+            ``True``).
+
+        :raises: `.SSHException` -- if the SSH2 negotiation fails, the host key
+            supplied by the server is incorrect, or authentication fails.
+
+        .. versionchanged:: 2.3
+            Added the ``gss_trust_dns`` argument.
+        """
+        if hostkey is not None:
+            # TODO: a more robust implementation would be to ask each key class
+            # for its nameS plural, and just use that.
+            # TODO: that could be used in a bunch of other spots too
+            if isinstance(hostkey, RSAKey):
+                self._preferred_keys = [
+                    "rsa-sha2-512",
+                    "rsa-sha2-256",
+                    "ssh-rsa",
+                ]
+            else:
+                self._preferred_keys = [hostkey.get_name()]
+
+        self.set_gss_host(
+            gss_host=gss_host,
+            trust_dns=gss_trust_dns,
+            gssapi_requested=gss_kex or gss_auth,
+        )
+
+        self.start_client()
+
+        # check host key if we were given one
+        # If GSS-API Key Exchange was performed, we are not required to check
+        # the host key.
+        if (hostkey is not None) and not gss_kex:
+            key = self.get_remote_server_key()
+            if (
+                key.get_name() != hostkey.get_name()
+                or key.asbytes() != hostkey.asbytes()
+            ):
+                self._log(DEBUG, "Bad host key from server")
+                self._log(
+                    DEBUG,
+                    "Expected: {}: {}".format(
+                        hostkey.get_name(), repr(hostkey.asbytes())
+                    ),
+                )
+                self._log(
+                    DEBUG,
+                    "Got     : {}: {}".format(
+                        key.get_name(), repr(key.asbytes())
+                    ),
+                )
+                raise SSHException("Bad host key from server")
+            self._log(
+                DEBUG, "Host key verified ({})".format(hostkey.get_name())
+            )
+
+        if (pkey is not None) or (password is not None) or gss_auth or gss_kex:
+            if gss_auth:
+                self._log(
+                    DEBUG, "Attempting GSS-API auth... (gssapi-with-mic)"
+                )  # noqa
+                self.auth_gssapi_with_mic(
+                    username, self.gss_host, gss_deleg_creds
+                )
+            elif gss_kex:
+                self._log(DEBUG, "Attempting GSS-API auth... (gssapi-keyex)")
+                self.auth_gssapi_keyex(username)
+            elif pkey is not None:
+                self._log(DEBUG, "Attempting public-key auth...")
+                self.auth_publickey(username, pkey)
+            else:
+                self._log(DEBUG, "Attempting password auth...")
+                self.auth_password(username, password)
+
+        return
+
+    def get_exception(self):
+        print("Get Exception")
+        """
+        Return any exception that happened during the last server request.
+        This can be used to fetch more specific error information after using
+        calls like `start_client`.  The exception (if any) is cleared after
+        this call.
+
+        :return:
+            an exception, or ``None`` if there is no stored exception.
+
+        .. versionadded:: 1.1
+        """
+        self.lock.acquire()
+        try:
+            e = self.saved_exception
+            self.saved_exception = None
+            return e
+        finally:
+            self.lock.release()
+
+    def set_subsystem_handler(self, name, handler, *args, **kwargs):
+        print("Set Subsystem Handler")
+        """
+        Set the handler class for a subsystem in server mode.  If a request
+        for this subsystem is made on an open ssh channel later, this handler
+        will be constructed and called -- see `.SubsystemHandler` for more
+        detailed documentation.
+
+        Any extra parameters (including keyword arguments) are saved and
+        passed to the `.SubsystemHandler` constructor later.
+
+        :param str name: name of the subsystem.
+        :param handler:
+            subclass of `.SubsystemHandler` that handles this subsystem.
+        """
+        try:
+            self.lock.acquire()
+            self.subsystem_table[name] = (handler, args, kwargs)
+        finally:
+            self.lock.release()
+
+    def is_authenticated(self):
+        print("Is Authenticated")
+        """
+        Return true if this session is active and authenticated.
+
+        :return:
+            True if the session is still open and has been authenticated
+            successfully; False if authentication failed and/or the session is
+            closed.
+        """
+        return (
+            self.active
+            and self.auth_handler is not None
+            and self.auth_handler.is_authenticated()
+        )
+
+    def get_username(self):
+        print("Get Username")
+        """
+        Return the username this connection is authenticated for.  If the
+        session is not authenticated (or authentication failed), this method
+        returns ``None``.
+
+        :return: username that was authenticated (a `str`), or ``None``.
+        """
+        if not self.active or (self.auth_handler is None):
+            return None
+        return self.auth_handler.get_username()
+
+    def get_banner(self):
+        print("Get Banner")
+        """
+        Return the banner supplied by the server upon connect. If no banner is
+        supplied, this method returns ``None``.
+
+        :returns: server supplied banner (`str`), or ``None``.
+
+        .. versionadded:: 1.13
+        """
+        if not self.active or (self.auth_handler is None):
+            return None
+        return self.auth_handler.banner
+
+    def auth_none(self, username):
+        print("Auth None")
+        """
+        Try to authenticate to the server using no authentication at all.
+        This will almost always fail.  It may be useful for determining the
+        list of authentication types supported by the server, by catching the
+        `.BadAuthenticationType` exception raised.
+
+        :param str username: the username to authenticate as
+        :return:
+            list of auth types permissible for the next stage of
+            authentication (normally empty)
+
+        :raises:
+            `.BadAuthenticationType` -- if "none" authentication isn't allowed
+            by the server for this user
+        :raises:
+            `.SSHException` -- if the authentication failed due to a network
+            error
+
+        .. versionadded:: 1.5
+        """
+        if (not self.active) or (not self.initial_kex_done):
+            raise SSHException("No existing session")
+        my_event = threading.Event()
+        self.auth_handler = AuthHandler(self)
+        self.auth_handler.auth_none(username, my_event)
+        return self.auth_handler.wait_for_response(my_event)
+
+    def auth_password(self, username, password, event=None, fallback=True):
+        print("Auth Password")
+        """
+        Authenticate to the server using a password.  The username and password
+        are sent over an encrypted link.
+
+        If an ``event`` is passed in, this method will return immediately, and
+        the event will be triggered once authentication succeeds or fails.  On
+        success, `is_authenticated` will return ``True``.  On failure, you may
+        use `get_exception` to get more detailed error information.
+
+        Since 1.1, if no event is passed, this method will block until the
+        authentication succeeds or fails.  On failure, an exception is raised.
+        Otherwise, the method simply returns.
+
+        Since 1.5, if no event is passed and ``fallback`` is ``True`` (the
+        default), if the server doesn't support plain password authentication
+        but does support so-called "keyboard-interactive" mode, an attempt
+        will be made to authenticate using this interactive mode.  If it fails,
+        the normal exception will be thrown as if the attempt had never been
+        made.  This is useful for some recent Gentoo and Debian distributions,
+        which turn off plain password authentication in a misguided belief
+        that interactive authentication is "more secure".  (It's not.)
+
+        If the server requires multi-step authentication (which is very rare),
+        this method will return a list of auth types permissible for the next
+        step.  Otherwise, in the normal case, an empty list is returned.
+
+        :param str username: the username to authenticate as
+        :param basestring password: the password to authenticate with
+        :param .threading.Event event:
+            an event to trigger when the authentication attempt is complete
+            (whether it was successful or not)
+        :param bool fallback:
+            ``True`` if an attempt at an automated "interactive" password auth
+            should be made if the server doesn't support normal password auth
+        :return:
+            list of auth types permissible for the next stage of
+            authentication (normally empty)
+
+        :raises:
+            `.BadAuthenticationType` -- if password authentication isn't
+            allowed by the server for this user (and no event was passed in)
+        :raises:
+            `.AuthenticationException` -- if the authentication failed (and no
+            event was passed in)
+        :raises: `.SSHException` -- if there was a network error
+        """
+        if (not self.active) or (not self.initial_kex_done):
+            # we should never try to send the password unless we're on a secure
+            # link
+            raise SSHException("No existing session")
+        if event is None:
+            my_event = threading.Event()
+        else:
+            my_event = event
+        self.auth_handler = AuthHandler(self)
+        self.auth_handler.auth_password(username, password, my_event)
+        if event is not None:
+            # caller wants to wait for event themselves
+            return []
+        try:
+            return self.auth_handler.wait_for_response(my_event)
+        except BadAuthenticationType as e:
+            # if password auth isn't allowed, but keyboard-interactive *is*,
+            # try to fudge it
+            if not fallback or ("keyboard-interactive" not in e.allowed_types):
+                raise
+            try:
+
+                def handler(title, instructions, fields):
+                    if len(fields) > 1:
+                        raise SSHException("Fallback authentication failed.")
+                    if len(fields) == 0:
+                        # for some reason, at least on os x, a 2nd request will
+                        # be made with zero fields requested.  maybe it's just
+                        # to try to fake out automated scripting of the exact
+                        # type we're doing here.  *shrug* :)
+                        return []
+                    return [password]
+
+                return self.auth_interactive(username, handler)
+            except SSHException:
+                # attempt failed; just raise the original exception
+                raise e
+
+    def auth_publickey(self, username, key, event=None):
+        print("Auth Public Key")
+        """
+        Authenticate to the server using a private key.  The key is used to
+        sign data from the server, so it must include the private part.
+
+        If an ``event`` is passed in, this method will return immediately, and
+        the event will be triggered once authentication succeeds or fails.  On
+        success, `is_authenticated` will return ``True``.  On failure, you may
+        use `get_exception` to get more detailed error information.
+
+        Since 1.1, if no event is passed, this method will block until the
+        authentication succeeds or fails.  On failure, an exception is raised.
+        Otherwise, the method simply returns.
+
+        If the server requires multi-step authentication (which is very rare),
+        this method will return a list of auth types permissible for the next
+        step.  Otherwise, in the normal case, an empty list is returned.
+
+        :param str username: the username to authenticate as
+        :param .PKey key: the private key to authenticate with
+        :param .threading.Event event:
+            an event to trigger when the authentication attempt is complete
+            (whether it was successful or not)
+        :return:
+            list of auth types permissible for the next stage of
+            authentication (normally empty)
+
+        :raises:
+            `.BadAuthenticationType` -- if public-key authentication isn't
+            allowed by the server for this user (and no event was passed in)
+        :raises:
+            `.AuthenticationException` -- if the authentication failed (and no
+            event was passed in)
+        :raises: `.SSHException` -- if there was a network error
+        """
+
+        print("auth_publickeyyyyyy")
+
+        if (not self.active) or (not self.initial_kex_done):
+            # we should never try to authenticate unless we're on a secure link
+            raise SSHException("No existing session")
+        if event is None:
+            my_event = threading.Event()
+        else:
+            my_event = event
+        self.auth_handler = AuthHandler(self)
+        self.auth_handler.auth_publickey(username, key, my_event)
+        if event is not None:
+            # caller wants to wait for event themselves
+            return []
+        return self.auth_handler.wait_for_response(my_event)
+
+    def auth_interactive(self, username, handler, submethods=""):
+        print("Auth Interactive")
+        """
+        Authenticate to the server interactively.  A handler is used to answer
+        arbitrary questions from the server.  On many servers, this is just a
+        dumb wrapper around PAM.
+
+        This method will block until the authentication succeeds or fails,
+        peroidically calling the handler asynchronously to get answers to
+        authentication questions.  The handler may be called more than once
+        if the server continues to ask questions.
+
+        The handler is expected to be a callable that will handle calls of the
+        form: ``handler(title, instructions, prompt_list)``.  The ``title`` is
+        meant to be a dialog-window title, and the ``instructions`` are user
+        instructions (both are strings).  ``prompt_list`` will be a list of
+        prompts, each prompt being a tuple of ``(str, bool)``.  The string is
+        the prompt and the boolean indicates whether the user text should be
+        echoed.
+
+        A sample call would thus be:
+        ``handler('title', 'instructions', [('Password:', False)])``.
+
+        The handler should return a list or tuple of answers to the server's
+        questions.
+
+        If the server requires multi-step authentication (which is very rare),
+        this method will return a list of auth types permissible for the next
+        step.  Otherwise, in the normal case, an empty list is returned.
+
+        :param str username: the username to authenticate as
+        :param callable handler: a handler for responding to server questions
+        :param str submethods: a string list of desired submethods (optional)
+        :return:
+            list of auth types permissible for the next stage of
+            authentication (normally empty).
+
+        :raises: `.BadAuthenticationType` -- if public-key authentication isn't
+            allowed by the server for this user
+        :raises: `.AuthenticationException` -- if the authentication failed
+        :raises: `.SSHException` -- if there was a network error
+
+        .. versionadded:: 1.5
+        """
+        if (not self.active) or (not self.initial_kex_done):
+            # we should never try to authenticate unless we're on a secure link
+            raise SSHException("No existing session")
+        my_event = threading.Event()
+        self.auth_handler = AuthHandler(self)
+        self.auth_handler.auth_interactive(
+            username, handler, my_event, submethods
+        )
+        return self.auth_handler.wait_for_response(my_event)
+
+    def auth_interactive_dumb(self, username, handler=None, submethods=""):
+        print("Auth Interactive Dumb")
+        """
+        Authenticate to the server interactively but dumber.
+        Just print the prompt and / or instructions to stdout and send back
+        the response. This is good for situations where partial auth is
+        achieved by key and then the user has to enter a 2fac token.
+        """
+
+        if not handler:
+
+            def handler(title, instructions, prompt_list):
+                answers = []
+                if title:
+                    print(title.strip())
+                if instructions:
+                    print(instructions.strip())
+                for prompt, show_input in prompt_list:
+                    print(prompt.strip(), end=" ")
+                    answers.append(input())
+                return answers
+
+        return self.auth_interactive(username, handler, submethods)
+
+    def auth_gssapi_with_mic(self, username, gss_host, gss_deleg_creds):
+        print("Auth GSSAPI with mic")
+        """
+        Authenticate to the Server using GSS-API / SSPI.
+
+        :param str username: The username to authenticate as
+        :param str gss_host: The target host
+        :param bool gss_deleg_creds: Delegate credentials or not
+        :return: list of auth types permissible for the next stage of
+                 authentication (normally empty)
+        :raises: `.BadAuthenticationType` -- if gssapi-with-mic isn't
+            allowed by the server (and no event was passed in)
+        :raises:
+            `.AuthenticationException` -- if the authentication failed (and no
+            event was passed in)
+        :raises: `.SSHException` -- if there was a network error
+        """
+        if (not self.active) or (not self.initial_kex_done):
+            # we should never try to authenticate unless we're on a secure link
+            raise SSHException("No existing session")
+        my_event = threading.Event()
+        self.auth_handler = AuthHandler(self)
+        self.auth_handler.auth_gssapi_with_mic(
+            username, gss_host, gss_deleg_creds, my_event
+        )
+        return self.auth_handler.wait_for_response(my_event)
+
+    def auth_gssapi_keyex(self, username):
+        print("Auth GSSAPI keyex")
+        """
+        Authenticate to the server with GSS-API/SSPI if GSS-API kex is in use.
+
+        :param str username: The username to authenticate as.
+        :returns:
+            a list of auth types permissible for the next stage of
+            authentication (normally empty)
+        :raises: `.BadAuthenticationType` --
+            if GSS-API Key Exchange was not performed (and no event was passed
+            in)
+        :raises: `.AuthenticationException` --
+            if the authentication failed (and no event was passed in)
+        :raises: `.SSHException` -- if there was a network error
+        """
+        if (not self.active) or (not self.initial_kex_done):
+            # we should never try to authenticate unless we're on a secure link
+            raise SSHException("No existing session")
+        my_event = threading.Event()
+        self.auth_handler = AuthHandler(self)
+        self.auth_handler.auth_gssapi_keyex(username, my_event)
+        return self.auth_handler.wait_for_response(my_event)
+
+    def set_log_channel(self, name):
+        print("Set log channel")
+        """
+        Set the channel for this transport's logging.  The default is
+        ``"paramiko.transport"`` but it can be set to anything you want. (See
+        the `.logging` module for more info.)  SSH Channels will log to a
+        sub-channel of the one specified.
+
+        :param str name: new channel name for logging
+
+        .. versionadded:: 1.1
+        """
+        self.log_name = name
+        self.logger = util.get_logger(name)
+        self.packetizer.set_log(self.logger)
+
+    def get_log_channel(self):
+        print("Get log channel")
+        """
+        Return the channel name used for this transport's logging.
+
+        :return: channel name as a `str`
+
+        .. versionadded:: 1.2
+        """
+        return self.log_name
+
+    def set_hexdump(self, hexdump):
+        print("Set hexdump")
+        """
+        Turn on/off logging a hex dump of protocol traffic at DEBUG level in
+        the logs.  Normally you would want this off (which is the default),
+        but if you are debugging something, it may be useful.
+
+        :param bool hexdump:
+            ``True`` to log protocol traffix (in hex) to the log; ``False``
+            otherwise.
+        """
+        self.packetizer.set_hexdump(hexdump)
+
+    def get_hexdump(self):
+        print("Get hexdump")
+        """
+        Return ``True`` if the transport is currently logging hex dumps of
+        protocol traffic.
+
+        :return: ``True`` if hex dumps are being logged, else ``False``.
+
+        .. versionadded:: 1.4
+        """
+        return self.packetizer.get_hexdump()
+
+    def use_compression(self, compress=True):
+        print("Use compression")
+        """
+        Turn on/off compression.  This will only have an affect before starting
+        the transport (ie before calling `connect`, etc).  By default,
+        compression is off since it negatively affects interactive sessions.
+
+        :param bool compress:
+            ``True`` to ask the remote client/server to compress traffic;
+            ``False`` to refuse compression
+
+        .. versionadded:: 1.5.2
+        """
+        if compress:
+            self._preferred_compression = ("zlib@openssh.com", "zlib", "none")
+        else:
+            self._preferred_compression = ("none",)
+
+    def getpeername(self):
+        print("Get peer name")
+        """
+        Return the address of the remote side of this Transport, if possible.
+
+        This is effectively a wrapper around ``getpeername`` on the underlying
+        socket.  If the socket-like object has no ``getpeername`` method, then
+        ``("unknown", 0)`` is returned.
+
+        :return:
+            the address of the remote host, if known, as a ``(str, int)``
+            tuple.
+        """
+        gp = getattr(self.sock, "getpeername", None)
+        if gp is None:
+            return "unknown", 0
+        return gp()
+
+    def stop_thread(self):
+        print("Stop thread")
+        self.active = False
+        self.packetizer.close()
+        # Keep trying to join() our main thread, quickly, until:
+        # * We join()ed successfully (self.is_alive() == False)
+        # * Or it looks like we've hit issue #520 (socket.recv hitting some
+        # race condition preventing it from timing out correctly), wherein
+        # our socket and packetizer are both closed (but where we'd
+        # otherwise be sitting forever on that recv()).
+        while (
+            self.is_alive()
+            and self is not threading.current_thread()
+            and not self.sock._closed
+            and not self.packetizer.closed
+        ):
+            self.join(0.1)
+
+    # internals...
+
+    def _log(self, level, msg, *args):
+        if issubclass(type(msg), list):
+            for m in msg:
+                self.logger.log(level, m)
+        else:
+            self.logger.log(level, msg, *args)
+
+    def _get_modulus_pack(self):
+        """used by KexGex to find primes for group exchange"""
+        return self._modulus_pack
+
+    def _next_channel(self):
+        """you are holding the lock"""
+        chanid = self._channel_counter
+        while self._channels.get(chanid) is not None:
+            self._channel_counter = (self._channel_counter + 1) & 0xFFFFFF
+            chanid = self._channel_counter
+        self._channel_counter = (self._channel_counter + 1) & 0xFFFFFF
+        return chanid
+
+    def _unlink_channel(self, chanid):
+        """used by a Channel to remove itself from the active channel list"""
+        self._channels.delete(chanid)
+
+    def _send_message(self, data):
+        print("send mesage")
+        self.packetizer.send_message(data)
+
+    def _send_user_message(self, data):
+        print("send user message")
+        """
+        send a message, but block if we're in key negotiation.  this is used
+        for user-initiated requests.
+        """
+        start = time.time()
+        while True:
+            self.clear_to_send.wait(0.1)
+            if not self.active:
+                self._log(
+                    DEBUG, "Dropping user packet because connection is dead."
+                )  # noqa
+                return
+            self.clear_to_send_lock.acquire()
+            if self.clear_to_send.is_set():
+                break
+            self.clear_to_send_lock.release()
+            if time.time() > start + self.clear_to_send_timeout:
+                raise SSHException(
+                    "Key-exchange timed out waiting for key negotiation"
+                )  # noqa
+        try:
+            self._send_message(data)
+        finally:
+            self.clear_to_send_lock.release()
+
+    def _set_K_H(self, k, h):
+        print("set K H")
+        """
+        Used by a kex obj to set the K (root key) and H (exchange hash).
+        """
+        self.K = k
+        self.H = h
+        if self.session_id is None:
+            self.session_id = h
+
+    def _expect_packet(self, *ptypes):
+        print("expect packet")
+        """
+        Used by a kex obj to register the next packet type it expects to see.
+        """
+        self._expected_packet = tuple(ptypes)
+
+    def _verify_key(self, host_key, sig):
+        print("verify key")
+        key = self._key_info[self.host_key_type](Message(host_key))
+        if key is None:
+            raise SSHException("Unknown host key type")
+        if not key.verify_ssh_sig(self.H, Message(sig)):
+            raise SSHException(
+                "Signature verification ({}) failed.".format(
+                    self.host_key_type
+                )
+            )  # noqa
+        self.host_key = key
+
+    def _compute_key(self, id, nbytes):
+        print("compute key")
+        """id is 'A' - 'F' for the various keys used by ssh"""
+        m = Message()
+        m.add_mpint(self.K)
+        m.add_bytes(self.H)
+        m.add_byte(b(id))
+        m.add_bytes(self.session_id)
+        # Fallback to SHA1 for kex engines that fail to specify a hex
+        # algorithm, or for e.g. transport tests that don't run kexinit.
+        hash_algo = getattr(self.kex_engine, "hash_algo", None)
+        hash_select_msg = "kex engine {} specified hash_algo {!r}".format(
+            self.kex_engine.__class__.__name__, hash_algo
+        )
+        if hash_algo is None:
+            hash_algo = sha1
+            hash_select_msg += ", falling back to sha1"
+        if not hasattr(self, "_logged_hash_selection"):
+            self._log(DEBUG, hash_select_msg)
+            setattr(self, "_logged_hash_selection", True)
+        out = sofar = hash_algo(m.asbytes()).digest()
+        while len(out) < nbytes:
+            m = Message()
+            m.add_mpint(self.K)
+            m.add_bytes(self.H)
+            m.add_bytes(sofar)
+            digest = hash_algo(m.asbytes()).digest()
+            out += digest
+            sofar += digest
+        return out[:nbytes]
+
+    def _get_cipher(self, name, key, iv, operation):
+        print("get cipher")
+        if name not in self._cipher_info:
+            raise SSHException("Unknown client cipher " + name)
+        else:
+            cipher = Cipher(
+                self._cipher_info[name]["class"](key),
+                self._cipher_info[name]["mode"](iv),
+                backend=default_backend(),
+            )
+            if operation is self._ENCRYPT:
+                return cipher.encryptor()
+            else:
+                return cipher.decryptor()
+
+    def _set_forward_agent_handler(self, handler):
+        print("set forward agent handler")
+        if handler is None:
+
+            def default_handler(channel):
+                self._queue_incoming_channel(channel)
+
+            self._forward_agent_handler = default_handler
+        else:
+            self._forward_agent_handler = handler
+
+    def _set_x11_handler(self, handler):
+        print("set x11 handler")
+        # only called if a channel has turned on x11 forwarding
+        if handler is None:
+            # by default, use the same mechanism as accept()
+            def default_handler(channel, src_addr_port):
+                self._queue_incoming_channel(channel)
+
+            self._x11_handler = default_handler
+        else:
+            self._x11_handler = handler
+
+    def _queue_incoming_channel(self, channel):
+        print("queue incoming channel")
+        self.lock.acquire()
+        try:
+            self.server_accepts.append(channel)
+            self.server_accept_cv.notify()
+        finally:
+            self.lock.release()
+
+    def _sanitize_window_size(self, window_size):
+        print("sanitize window size")
+        if window_size is None:
+            window_size = self.default_window_size
+        return clamp_value(MIN_WINDOW_SIZE, window_size, MAX_WINDOW_SIZE)
+
+    def _sanitize_packet_size(self, max_packet_size):
+        print("sanitize packet size")
+        if max_packet_size is None:
+            max_packet_size = self.default_max_packet_size
+        return clamp_value(MIN_PACKET_SIZE, max_packet_size, MAX_WINDOW_SIZE)
+
+    def _ensure_authed(self, ptype, message):
+        print("ensure authed")
+        """
+        Checks message type against current auth state.
+
+        If server mode, and auth has not succeeded, and the message is of a
+        post-auth type (channel open or global request) an appropriate error
+        response Message is crafted and returned to caller for sending.
+
+        Otherwise (client mode, authed, or pre-auth message) returns None.
+        """
+        if (
+            not self.server_mode
+            or ptype <= HIGHEST_USERAUTH_MESSAGE_ID
+            or self.is_authenticated()
+        ):
+            return None
+        # WELP. We must be dealing with someone trying to do non-auth things
+        # without being authed. Tell them off, based on message class.
+        reply = Message()
+        # Global requests have no details, just failure.
+        if ptype == MSG_GLOBAL_REQUEST:
+            reply.add_byte(cMSG_REQUEST_FAILURE)
+        # Channel opens let us reject w/ a specific type + message.
+        elif ptype == MSG_CHANNEL_OPEN:
+            kind = message.get_text()  # noqa
+            chanid = message.get_int()
+            reply.add_byte(cMSG_CHANNEL_OPEN_FAILURE)
+            reply.add_int(chanid)
+            reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED)
+            reply.add_string("")
+            reply.add_string("en")
+        # NOTE: Post-open channel messages do not need checking; the above will
+        # reject attempts to open channels, meaning that even if a malicious
+        # user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under
+        # the logic that handles unknown channel IDs (as the channel list will
+        # be empty.)
+        return reply
+
+    def run(self):
+        print("run")
+        # (use the exposed "run" method, because if we specify a thread target
+        # of a private method, threading.Thread will keep a reference to it
+        # indefinitely, creating a GC cycle and not letting Transport ever be
+        # GC'd. it's a bug in Thread.)
+
+        # Hold reference to 'sys' so we can test sys.modules to detect
+        # interpreter shutdown.
+        self.sys = sys
+
+        # active=True occurs before the thread is launched, to avoid a race
+        _active_threads.append(self)
+        tid = hex(id(self) & xffffffff)
+        if self.server_mode:
+            self._log(DEBUG, "starting thread (server mode): {}".format(tid))
+        else:
+            self._log(DEBUG, "starting thread (client mode): {}".format(tid))
+        try:
+            try:
+                self.packetizer.write_all(b(self.local_version + "\r\n"))
+                self._log(
+                    DEBUG,
+                    "Local version/idstring: {}".format(self.local_version),
+                )  # noqa
+                self._check_banner()
+                # The above is actually very much part of the handshake, but
+                # sometimes the banner can be read but the machine is not
+                # responding, for example when the remote ssh daemon is loaded
+                # in to memory but we can not read from the disk/spawn a new
+                # shell.
+                # Make sure we can specify a timeout for the initial handshake.
+                # Re-use the banner timeout for now.
+                self.packetizer.start_handshake(self.handshake_timeout)
+                self._send_kex_init()
+                self._expect_packet(MSG_KEXINIT)
+
+                while self.active:
+                    if self.packetizer.need_rekey() and not self.in_kex:
+                        self._send_kex_init()
+                    try:
+                        ptype, m = self.packetizer.read_message()
+                    except NeedRekeyException:
+                        continue
+                    if ptype == MSG_IGNORE:
+                        continue
+                    elif ptype == MSG_DISCONNECT:
+                        self._parse_disconnect(m)
+                        break
+                    elif ptype == MSG_DEBUG:
+                        self._parse_debug(m)
+                        continue
+                    if len(self._expected_packet) > 0:
+                        if ptype not in self._expected_packet:
+                            raise SSHException(
+                                "Expecting packet from {!r}, got {:d}".format(
+                                    self._expected_packet, ptype
+                                )
+                            )  # noqa
+                        self._expected_packet = tuple()
+                        if (ptype >= 30) and (ptype <= 41):
+                            self.kex_engine.parse_next(ptype, m)
+                            continue
+
+                    if ptype in self._handler_table:
+                        error_msg = self._ensure_authed(ptype, m)
+                        if error_msg:
+                            self._send_message(error_msg)
+                        else:
+                            self._handler_table[ptype](self, m)
+                    elif ptype in self._channel_handler_table:
+                        chanid = m.get_int()
+                        chan = self._channels.get(chanid)
+                        if chan is not None:
+                            self._channel_handler_table[ptype](chan, m)
+                        elif chanid in self.channels_seen:
+                            self._log(
+                                DEBUG,
+                                "Ignoring message for dead channel {:d}".format(  # noqa
+                                    chanid
+                                ),
+                            )
+                        else:
+                            self._log(
+                                ERROR,
+                                "Channel request for unknown channel {:d}".format(  # noqa
+                                    chanid
+                                ),
+                            )
+                            break
+                    elif (
+                        self.auth_handler is not None
+                        and ptype in self.auth_handler._handler_table
+                    ):
+                        handler = self.auth_handler._handler_table[ptype]
+                        handler(self.auth_handler, m)
+                        if len(self._expected_packet) > 0:
+                            continue
+                    else:
+                        # Respond with "I don't implement this particular
+                        # message type" message (unless the message type was
+                        # itself literally MSG_UNIMPLEMENTED, in which case, we
+                        # just shut up to avoid causing a useless loop).
+                        name = MSG_NAMES[ptype]
+                        warning = "Oops, unhandled type {} ({!r})".format(
+                            ptype, name
+                        )
+                        self._log(WARNING, warning)
+                        if ptype != MSG_UNIMPLEMENTED:
+                            msg = Message()
+                            msg.add_byte(cMSG_UNIMPLEMENTED)
+                            msg.add_int(m.seqno)
+                            self._send_message(msg)
+                    self.packetizer.complete_handshake()
+            except SSHException as e:
+                self._log(
+                    ERROR,
+                    "Exception ({}): {}".format(
+                        "server" if self.server_mode else "client", e
+                    ),
+                )
+                self._log(ERROR, util.tb_strings())
+                self.saved_exception = e
+            except EOFError as e:
+                self._log(DEBUG, "EOF in transport thread")
+                self.saved_exception = e
+            except socket.error as e:
+                if type(e.args) is tuple:
+                    if e.args:
+                        emsg = "{} ({:d})".format(e.args[1], e.args[0])
+                    else:  # empty tuple, e.g. socket.timeout
+                        emsg = str(e) or repr(e)
+                else:
+                    emsg = e.args
+                self._log(ERROR, "Socket exception: " + emsg)
+                self.saved_exception = e
+            except Exception as e:
+                self._log(ERROR, "Unknown exception: " + str(e))
+                self._log(ERROR, util.tb_strings())
+                self.saved_exception = e
+            _active_threads.remove(self)
+            for chan in list(self._channels.values()):
+                chan._unlink()
+            if self.active:
+                self.active = False
+                self.packetizer.close()
+                if self.completion_event is not None:
+                    self.completion_event.set()
+                if self.auth_handler is not None:
+                    self.auth_handler.abort()
+                for event in self.channel_events.values():
+                    event.set()
+                try:
+                    self.lock.acquire()
+                    self.server_accept_cv.notify()
+                finally:
+                    self.lock.release()
+            self.sock.close()
+        except:
+            # Don't raise spurious 'NoneType has no attribute X' errors when we
+            # wake up during interpreter shutdown. Or rather -- raise
+            # everything *if* sys.modules (used as a convenient sentinel)
+            # appears to still exist.
+            if self.sys.modules is not None:
+                raise
+
+    def _log_agreement(self, which, local, remote):
+        print("local")
+        # Log useful, non-duplicative line re: an agreed-upon algorithm.
+        # Old code implied algorithms could be asymmetrical (different for
+        # inbound vs outbound) so we preserve that possibility.
+        msg = "{}: ".format(which)
+        if local == remote:
+            msg += local
+        else:
+            msg += "local={}, remote={}".format(local, remote)
+        self._log(DEBUG, msg)
+
+    # protocol stages
+
+    def _negotiate_keys(self, m):
+        print("negotiate keys")
+        # throws SSHException on anything unusual
+        self.clear_to_send_lock.acquire()
+        try:
+            self.clear_to_send.clear()
+        finally:
+            self.clear_to_send_lock.release()
+        if self.local_kex_init is None:
+            # remote side wants to renegotiate
+            self._send_kex_init()
+        self._parse_kex_init(m)
+        self.kex_engine.start_kex()
+
+    def _check_banner(self):
+        print("check banner")
+        # this is slow, but we only have to do it once
+        for i in range(100):
+            # give them 15 seconds for the first line, then just 2 seconds
+            # each additional line.  (some sites have very high latency.)
+            if i == 0:
+                timeout = self.banner_timeout
+            else:
+                timeout = 2
+            try:
+                buf = self.packetizer.readline(timeout)
+            except ProxyCommandFailure:
+                raise
+            except Exception as e:
+                raise SSHException(
+                    "Error reading SSH protocol banner" + str(e)
+                )
+            if buf[:4] == "SSH-":
+                break
+            self._log(DEBUG, "Banner: " + buf)
+        if buf[:4] != "SSH-":
+            raise SSHException('Indecipherable protocol version "' + buf + '"')
+        # save this server version string for later
+        self.remote_version = buf
+        self._log(DEBUG, "Remote version/idstring: {}".format(buf))
+        # pull off any attached comment
+        # NOTE: comment used to be stored in a variable and then...never used.
+        # since 2003. ca 877cd974b8182d26fa76d566072917ea67b64e67
+        i = buf.find(" ")
+        if i >= 0:
+            buf = buf[:i]
+        # parse out version string and make sure it matches
+        segs = buf.split("-", 2)
+        if len(segs) < 3:
+            raise SSHException("Invalid SSH banner")
+        version = segs[1]
+        client = segs[2]
+        if version != "1.99" and version != "2.0":
+            msg = "Incompatible version ({} instead of 2.0)"
+            raise IncompatiblePeer(msg.format(version))
+        msg = "Connected (version {}, client {})".format(version, client)
+        self._log(INFO, msg)
+
+    def _send_kex_init(self):
+        print("send kex init")
+        """
+        announce to the other side that we'd like to negotiate keys, and what
+        kind of key negotiation we support.
+        """
+        self.clear_to_send_lock.acquire()
+        try:
+            self.clear_to_send.clear()
+        finally:
+            self.clear_to_send_lock.release()
+        self.gss_kex_used = False
+        self.in_kex = True
+        kex_algos = list(self.preferred_kex)
+        if self.server_mode:
+            mp_required_prefix = "diffie-hellman-group-exchange-sha"
+            kex_mp = [k for k in kex_algos if k.startswith(mp_required_prefix)]
+            if (self._modulus_pack is None) and (len(kex_mp) > 0):
+                # can't do group-exchange if we don't have a pack of potential
+                # primes
+                pkex = [
+                    k
+                    for k in self.get_security_options().kex
+                    if not k.startswith(mp_required_prefix)
+                ]
+                self.get_security_options().kex = pkex
+            available_server_keys = list(
+                filter(
+                    list(self.server_key_dict.keys()).__contains__,
+                    # TODO: ensure tests will catch if somebody streamlines
+                    # this by mistake - case is the admittedly silly one where
+                    # the only calls to add_server_key() contain keys which
+                    # were filtered out of the below via disabled_algorithms.
+                    # If this is streamlined, we would then be allowing the
+                    # disabled algorithm(s) for hostkey use
+                    # TODO: honestly this prob just wants to get thrown out
+                    # when we make kex configuration more straightforward
+                    self.preferred_keys,
+                )
+            )
+        else:
+            available_server_keys = self.preferred_keys
+            # Signal support for MSG_EXT_INFO.
+            # NOTE: doing this here handily means we don't even consider this
+            # value when agreeing on real kex algo to use (which is a common
+            # pitfall when adding this apparently).
+            kex_algos.append("ext-info-c")
+
+        m = Message()
+        m.add_byte(cMSG_KEXINIT)
+        m.add_bytes(os.urandom(16))
+        m.add_list(kex_algos)
+        m.add_list(available_server_keys)
+        m.add_list(self.preferred_ciphers)
+        m.add_list(self.preferred_ciphers)
+        m.add_list(self.preferred_macs)
+        m.add_list(self.preferred_macs)
+        m.add_list(self.preferred_compression)
+        m.add_list(self.preferred_compression)
+        m.add_string(bytes())
+        m.add_string(bytes())
+        m.add_boolean(False)
+        m.add_int(0)
+        # save a copy for later (needed to compute a hash)
+        self.local_kex_init = self._latest_kex_init = m.asbytes()
+        self._send_message(m)
+
+    def _really_parse_kex_init(self, m, ignore_first_byte=False):
+        print("really parse kex init")
+        parsed = {}
+        if ignore_first_byte:
+            m.get_byte()
+        m.get_bytes(16)  # cookie, discarded
+        parsed["kex_algo_list"] = m.get_list()
+        parsed["server_key_algo_list"] = m.get_list()
+        parsed["client_encrypt_algo_list"] = m.get_list()
+        parsed["server_encrypt_algo_list"] = m.get_list()
+        parsed["client_mac_algo_list"] = m.get_list()
+        parsed["server_mac_algo_list"] = m.get_list()
+        parsed["client_compress_algo_list"] = m.get_list()
+        parsed["server_compress_algo_list"] = m.get_list()
+        parsed["client_lang_list"] = m.get_list()
+        parsed["server_lang_list"] = m.get_list()
+        parsed["kex_follows"] = m.get_boolean()
+        m.get_int()  # unused
+        return parsed
+
+    def _get_latest_kex_init(self):
+        print("get latest kex init")
+        return self._really_parse_kex_init(
+            Message(self._latest_kex_init), ignore_first_byte=True
+        )
+
+    def _parse_kex_init(self, m):
+        print("parse kex init")
+        parsed = self._really_parse_kex_init(m)
+        kex_algo_list = parsed["kex_algo_list"]
+        server_key_algo_list = parsed["server_key_algo_list"]
+        client_encrypt_algo_list = parsed["client_encrypt_algo_list"]
+        server_encrypt_algo_list = parsed["server_encrypt_algo_list"]
+        client_mac_algo_list = parsed["client_mac_algo_list"]
+        server_mac_algo_list = parsed["server_mac_algo_list"]
+        client_compress_algo_list = parsed["client_compress_algo_list"]
+        server_compress_algo_list = parsed["server_compress_algo_list"]
+        client_lang_list = parsed["client_lang_list"]
+        server_lang_list = parsed["server_lang_list"]
+        kex_follows = parsed["kex_follows"]
+
+        self._log(DEBUG, "=== Key exchange possibilities ===")
+        for prefix, value in (
+            ("kex algos", kex_algo_list),
+            ("server key", server_key_algo_list),
+            # TODO: shouldn't these two lines say "cipher" to match usual
+            # terminology (including elsewhere in paramiko!)?
+            ("client encrypt", client_encrypt_algo_list),
+            ("server encrypt", server_encrypt_algo_list),
+            ("client mac", client_mac_algo_list),
+            ("server mac", server_mac_algo_list),
+            ("client compress", client_compress_algo_list),
+            ("server compress", server_compress_algo_list),
+            ("client lang", client_lang_list),
+            ("server lang", server_lang_list),
+        ):
+            if value == [""]:
+                value = ["<none>"]
+            value = ", ".join(value)
+            self._log(DEBUG, "{}: {}".format(prefix, value))
+        self._log(DEBUG, "kex follows: {}".format(kex_follows))
+        self._log(DEBUG, "=== Key exchange agreements ===")
+
+        # Strip out ext-info "kex algo"
+        self._remote_ext_info = None
+        if kex_algo_list[-1].startswith("ext-info-"):
+            self._remote_ext_info = kex_algo_list.pop()
+
+        # as a server, we pick the first item in the client's list that we
+        # support.
+        # as a client, we pick the first item in our list that the server
+        # supports.
+        if self.server_mode:
+            agreed_kex = list(
+                filter(self.preferred_kex.__contains__, kex_algo_list)
+            )
+        else:
+            agreed_kex = list(
+                filter(kex_algo_list.__contains__, self.preferred_kex)
+            )
+        if len(agreed_kex) == 0:
+            # TODO: do an auth-overhaul style aggregate exception here?
+            # TODO: would let us streamline log output & show all failures up
+            # front
+            raise IncompatiblePeer(
+                "Incompatible ssh peer (no acceptable kex algorithm)"
+            )  # noqa
+        self.kex_engine = self._kex_info[agreed_kex[0]](self)
+        self._log(DEBUG, "Kex: {}".format(agreed_kex[0]))
+
+        if self.server_mode:
+            available_server_keys = list(
+                filter(
+                    list(self.server_key_dict.keys()).__contains__,
+                    self.preferred_keys,
+                )
+            )
+            agreed_keys = list(
+                filter(
+                    available_server_keys.__contains__, server_key_algo_list
+                )
+            )
+        else:
+            agreed_keys = list(
+                filter(server_key_algo_list.__contains__, self.preferred_keys)
+            )
+        if len(agreed_keys) == 0:
+            raise IncompatiblePeer(
+                "Incompatible ssh peer (no acceptable host key)"
+            )  # noqa
+        self.host_key_type = agreed_keys[0]
+        if self.server_mode and (self.get_server_key() is None):
+            raise IncompatiblePeer(
+                "Incompatible ssh peer (can't match requested host key type)"
+            )  # noqa
+        self._log_agreement("HostKey", agreed_keys[0], agreed_keys[0])
+
+        if self.server_mode:
+            agreed_local_ciphers = list(
+                filter(
+                    self.preferred_ciphers.__contains__,
+                    server_encrypt_algo_list,
+                )
+            )
+            agreed_remote_ciphers = list(
+                filter(
+                    self.preferred_ciphers.__contains__,
+                    client_encrypt_algo_list,
+                )
+            )
+        else:
+            agreed_local_ciphers = list(
+                filter(
+                    client_encrypt_algo_list.__contains__,
+                    self.preferred_ciphers,
+                )
+            )
+            agreed_remote_ciphers = list(
+                filter(
+                    server_encrypt_algo_list.__contains__,
+                    self.preferred_ciphers,
+                )
+            )
+        if len(agreed_local_ciphers) == 0 or len(agreed_remote_ciphers) == 0:
+            raise IncompatiblePeer(
+                "Incompatible ssh server (no acceptable ciphers)"
+            )  # noqa
+        self.local_cipher = agreed_local_ciphers[0]
+        self.remote_cipher = agreed_remote_ciphers[0]
+        self._log_agreement(
+            "Cipher", local=self.local_cipher, remote=self.remote_cipher
+        )
+
+        if self.server_mode:
+            agreed_remote_macs = list(
+                filter(self.preferred_macs.__contains__, client_mac_algo_list)
+            )
+            agreed_local_macs = list(
+                filter(self.preferred_macs.__contains__, server_mac_algo_list)
+            )
+        else:
+            agreed_local_macs = list(
+                filter(client_mac_algo_list.__contains__, self.preferred_macs)
+            )
+            agreed_remote_macs = list(
+                filter(server_mac_algo_list.__contains__, self.preferred_macs)
+            )
+        if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0):
+            raise IncompatiblePeer(
+                "Incompatible ssh server (no acceptable macs)"
+            )
+        self.local_mac = agreed_local_macs[0]
+        self.remote_mac = agreed_remote_macs[0]
+        self._log_agreement(
+            "MAC", local=self.local_mac, remote=self.remote_mac
+        )
+
+        if self.server_mode:
+            agreed_remote_compression = list(
+                filter(
+                    self.preferred_compression.__contains__,
+                    client_compress_algo_list,
+                )
+            )
+            agreed_local_compression = list(
+                filter(
+                    self.preferred_compression.__contains__,
+                    server_compress_algo_list,
+                )
+            )
+        else:
+            agreed_local_compression = list(
+                filter(
+                    client_compress_algo_list.__contains__,
+                    self.preferred_compression,
+                )
+            )
+            agreed_remote_compression = list(
+                filter(
+                    server_compress_algo_list.__contains__,
+                    self.preferred_compression,
+                )
+            )
+        if (
+            len(agreed_local_compression) == 0
+            or len(agreed_remote_compression) == 0
+        ):
+            msg = "Incompatible ssh server (no acceptable compression)"
+            msg += " {!r} {!r} {!r}"
+            raise IncompatiblePeer(
+                msg.format(
+                    agreed_local_compression,
+                    agreed_remote_compression,
+                    self.preferred_compression,
+                )
+            )
+        self.local_compression = agreed_local_compression[0]
+        self.remote_compression = agreed_remote_compression[0]
+        self._log_agreement(
+            "Compression",
+            local=self.local_compression,
+            remote=self.remote_compression,
+        )
+        self._log(DEBUG, "=== End of kex handshake ===")
+
+        # save for computing hash later...
+        # now wait!  openssh has a bug (and others might too) where there are
+        # actually some extra bytes (one NUL byte in openssh's case) added to
+        # the end of the packet but not parsed.  turns out we need to throw
+        # away those bytes because they aren't part of the hash.
+        self.remote_kex_init = cMSG_KEXINIT + m.get_so_far()
+
+    def _activate_inbound(self):
+        print("_activate_inbound")
+        """switch on newly negotiated encryption parameters for
+        inbound traffic"""
+        block_size = self._cipher_info[self.remote_cipher]["block-size"]
+        if self.server_mode:
+            IV_in = self._compute_key("A", block_size)
+            key_in = self._compute_key(
+                "C", self._cipher_info[self.remote_cipher]["key-size"]
+            )
+        else:
+            IV_in = self._compute_key("B", block_size)
+            key_in = self._compute_key(
+                "D", self._cipher_info[self.remote_cipher]["key-size"]
+            )
+        engine = self._get_cipher(
+            self.remote_cipher, key_in, IV_in, self._DECRYPT
+        )
+        etm = "etm@openssh.com" in self.remote_mac
+        mac_size = self._mac_info[self.remote_mac]["size"]
+        mac_engine = self._mac_info[self.remote_mac]["class"]
+        # initial mac keys are done in the hash's natural size (not the
+        # potentially truncated transmission size)
+        if self.server_mode:
+            mac_key = self._compute_key("E", mac_engine().digest_size)
+        else:
+            mac_key = self._compute_key("F", mac_engine().digest_size)
+        self.packetizer.set_inbound_cipher(
+            engine, block_size, mac_engine, mac_size, mac_key, etm=etm
+        )
+        compress_in = self._compression_info[self.remote_compression][1]
+        if compress_in is not None and (
+            self.remote_compression != "zlib@openssh.com" or self.authenticated
+        ):
+            self._log(DEBUG, "Switching on inbound compression ...")
+            self.packetizer.set_inbound_compressor(compress_in())
+
+    def _activate_outbound(self):
+        print("_activate_outbound")
+        """switch on newly negotiated encryption parameters for
+        outbound traffic"""
+        m = Message()
+        m.add_byte(cMSG_NEWKEYS)
+        self._send_message(m)
+        block_size = self._cipher_info[self.local_cipher]["block-size"]
+        if self.server_mode:
+            IV_out = self._compute_key("B", block_size)
+            key_out = self._compute_key(
+                "D", self._cipher_info[self.local_cipher]["key-size"]
+            )
+        else:
+            IV_out = self._compute_key("A", block_size)
+            key_out = self._compute_key(
+                "C", self._cipher_info[self.local_cipher]["key-size"]
+            )
+        engine = self._get_cipher(
+            self.local_cipher, key_out, IV_out, self._ENCRYPT
+        )
+        etm = "etm@openssh.com" in self.local_mac
+        mac_size = self._mac_info[self.local_mac]["size"]
+        mac_engine = self._mac_info[self.local_mac]["class"]
+        # initial mac keys are done in the hash's natural size (not the
+        # potentially truncated transmission size)
+        if self.server_mode:
+            mac_key = self._compute_key("F", mac_engine().digest_size)
+        else:
+            mac_key = self._compute_key("E", mac_engine().digest_size)
+        sdctr = self.local_cipher.endswith("-ctr")
+        self.packetizer.set_outbound_cipher(
+            engine, block_size, mac_engine, mac_size, mac_key, sdctr, etm=etm
+        )
+        compress_out = self._compression_info[self.local_compression][0]
+        if compress_out is not None and (
+            self.local_compression != "zlib@openssh.com" or self.authenticated
+        ):
+            self._log(DEBUG, "Switching on outbound compression ...")
+            self.packetizer.set_outbound_compressor(compress_out())
+        if not self.packetizer.need_rekey():
+            self.in_kex = False
+        # If client indicated extension support, send that packet immediately
+        if (
+            self.server_mode
+            and self.server_sig_algs
+            and self._remote_ext_info == "ext-info-c"
+        ):
+            extensions = {"server-sig-algs": ",".join(self.preferred_pubkeys)}
+            m = Message()
+            m.add_byte(cMSG_EXT_INFO)
+            m.add_int(len(extensions))
+            for name, value in sorted(extensions.items()):
+                m.add_string(name)
+                m.add_string(value)
+            self._send_message(m)
+        # we always expect to receive NEWKEYS now
+        self._expect_packet(MSG_NEWKEYS)
+
+    def _auth_trigger(self):
+        print("_auth_trigger")
+        self.authenticated = True
+        # delayed initiation of compression
+        if self.local_compression == "zlib@openssh.com":
+            compress_out = self._compression_info[self.local_compression][0]
+            self._log(DEBUG, "Switching on outbound compression ...")
+            self.packetizer.set_outbound_compressor(compress_out())
+        if self.remote_compression == "zlib@openssh.com":
+            compress_in = self._compression_info[self.remote_compression][1]
+            self._log(DEBUG, "Switching on inbound compression ...")
+            self.packetizer.set_inbound_compressor(compress_in())
+
+    def _parse_ext_info(self, msg):
+        print("_parse_ext_info")
+        # Packet is a count followed by that many key-string to possibly-bytes
+        # pairs.
+        extensions = {}
+        for _ in range(msg.get_int()):
+            name = msg.get_text()
+            value = msg.get_string()
+            extensions[name] = value
+        self._log(DEBUG, "Got EXT_INFO: {}".format(extensions))
+        # NOTE: this should work ok in cases where a server sends /two/ such
+        # messages; the RFC explicitly states a 2nd one should overwrite the
+        # 1st.
+        self.server_extensions = extensions
+
+    def _parse_newkeys(self, m):
+        print("_parse_newkeys")
+        self._log(DEBUG, "Switch to new keys ...")
+        self._activate_inbound()
+        # can also free a bunch of stuff here
+        self.local_kex_init = self.remote_kex_init = None
+        self.K = None
+        self.kex_engine = None
+        if self.server_mode and (self.auth_handler is None):
+            # create auth handler for server mode
+            self.auth_handler = AuthHandler(self)
+        if not self.initial_kex_done:
+            # this was the first key exchange
+            self.initial_kex_done = True
+        # send an event?
+        if self.completion_event is not None:
+            self.completion_event.set()
+        # it's now okay to send data again (if this was a re-key)
+        if not self.packetizer.need_rekey():
+            self.in_kex = False
+        self.clear_to_send_lock.acquire()
+        try:
+            self.clear_to_send.set()
+        finally:
+            self.clear_to_send_lock.release()
+        return
+
+    def _parse_disconnect(self, m):
+        print("_parse_disconnect")
+        code = m.get_int()
+        desc = m.get_text()
+        self._log(INFO, "Disconnect (code {:d}): {}".format(code, desc))
+
+    def _parse_global_request(self, m):
+        print("_parse_global_request")
+        kind = m.get_text()
+        self._log(DEBUG, 'Received global request "{}"'.format(kind))
+        want_reply = m.get_boolean()
+        if not self.server_mode:
+            self._log(
+                DEBUG,
+                'Rejecting "{}" global request from server.'.format(kind),
+            )
+            ok = False
+        elif kind == "tcpip-forward":
+            address = m.get_text()
+            port = m.get_int()
+            ok = self.server_object.check_port_forward_request(address, port)
+            if ok:
+                ok = (ok,)
+        elif kind == "cancel-tcpip-forward":
+            address = m.get_text()
+            port = m.get_int()
+            self.server_object.cancel_port_forward_request(address, port)
+            ok = True
+        else:
+            ok = self.server_object.check_global_request(kind, m)
+        extra = ()
+        if type(ok) is tuple:
+            extra = ok
+            ok = True
+        if want_reply:
+            msg = Message()
+            if ok:
+                msg.add_byte(cMSG_REQUEST_SUCCESS)
+                msg.add(*extra)
+            else:
+                msg.add_byte(cMSG_REQUEST_FAILURE)
+            self._send_message(msg)
+
+    def _parse_request_success(self, m):
+        print("_parse_request_success")
+        self._log(DEBUG, "Global request successful.")
+        self.global_response = m
+        if self.completion_event is not None:
+            self.completion_event.set()
+
+    def _parse_request_failure(self, m):
+        print("_parse_request_failure")
+        self._log(DEBUG, "Global request denied.")
+        self.global_response = None
+        if self.completion_event is not None:
+            self.completion_event.set()
+
+    def _parse_channel_open_success(self, m):
+        print("_parse_channel_open_success")
+        chanid = m.get_int()
+        server_chanid = m.get_int()
+        server_window_size = m.get_int()
+        server_max_packet_size = m.get_int()
+        chan = self._channels.get(chanid)
+        if chan is None:
+            self._log(WARNING, "Success for unrequested channel! [??]")
+            return
+        self.lock.acquire()
+        try:
+            chan._set_remote_channel(
+                server_chanid, server_window_size, server_max_packet_size
+            )
+            self._log(DEBUG, "Secsh channel {:d} opened.".format(chanid))
+            if chanid in self.channel_events:
+                self.channel_events[chanid].set()
+                del self.channel_events[chanid]
+        finally:
+            self.lock.release()
+        return
+
+    def _parse_channel_open_failure(self, m):
+        print("_parse_channel_open_failure")
+        chanid = m.get_int()
+        reason = m.get_int()
+        reason_str = m.get_text()
+        m.get_text()  # ignored language
+        reason_text = CONNECTION_FAILED_CODE.get(reason, "(unknown code)")
+        self._log(
+            ERROR,
+            "Secsh channel {:d} open FAILED: {}: {}".format(
+                chanid, reason_str, reason_text
+            ),
+        )
+        self.lock.acquire()
+        try:
+            self.saved_exception = ChannelException(reason, reason_text)
+            if chanid in self.channel_events:
+                self._channels.delete(chanid)
+                if chanid in self.channel_events:
+                    self.channel_events[chanid].set()
+                    del self.channel_events[chanid]
+        finally:
+            self.lock.release()
+        return
+
+    def _parse_channel_open(self, m):
+        print("_parse_channel_open")
+        kind = m.get_text()
+        chanid = m.get_int()
+        initial_window_size = m.get_int()
+        max_packet_size = m.get_int()
+        reject = False
+        if (
+            kind == "auth-agent@openssh.com"
+            and self._forward_agent_handler is not None
+        ):
+            self._log(DEBUG, "Incoming forward agent connection")
+            self.lock.acquire()
+            try:
+                my_chanid = self._next_channel()
+            finally:
+                self.lock.release()
+        elif (kind == "x11") and (self._x11_handler is not None):
+            origin_addr = m.get_text()
+            origin_port = m.get_int()
+            self._log(
+                DEBUG,
+                "Incoming x11 connection from {}:{:d}".format(
+                    origin_addr, origin_port
+                ),
+            )
+            self.lock.acquire()
+            try:
+                my_chanid = self._next_channel()
+            finally:
+                self.lock.release()
+        elif (kind == "forwarded-tcpip") and (self._tcp_handler is not None):
+            server_addr = m.get_text()
+            server_port = m.get_int()
+            origin_addr = m.get_text()
+            origin_port = m.get_int()
+            self._log(
+                DEBUG,
+                "Incoming tcp forwarded connection from {}:{:d}".format(
+                    origin_addr, origin_port
+                ),
+            )
+            self.lock.acquire()
+            try:
+                my_chanid = self._next_channel()
+            finally:
+                self.lock.release()
+        elif not self.server_mode:
+            self._log(
+                DEBUG,
+                'Rejecting "{}" channel request from server.'.format(kind),
+            )
+            reject = True
+            reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
+        else:
+            self.lock.acquire()
+            try:
+                my_chanid = self._next_channel()
+            finally:
+                self.lock.release()
+            if kind == "direct-tcpip":
+                # handle direct-tcpip requests coming from the client
+                dest_addr = m.get_text()
+                dest_port = m.get_int()
+                origin_addr = m.get_text()
+                origin_port = m.get_int()
+                reason = self.server_object.check_channel_direct_tcpip_request(
+                    my_chanid,
+                    (origin_addr, origin_port),
+                    (dest_addr, dest_port),
+                )
+            else:
+                reason = self.server_object.check_channel_request(
+                    kind, my_chanid
+                )
+            if reason != OPEN_SUCCEEDED:
+                self._log(
+                    DEBUG,
+                    'Rejecting "{}" channel request from client.'.format(kind),
+                )
+                reject = True
+        if reject:
+            msg = Message()
+            msg.add_byte(cMSG_CHANNEL_OPEN_FAILURE)
+            msg.add_int(chanid)
+            msg.add_int(reason)
+            msg.add_string("")
+            msg.add_string("en")
+            self._send_message(msg)
+            return
+
+        chan = Channel(my_chanid)
+        self.lock.acquire()
+        try:
+            self._channels.put(my_chanid, chan)
+            self.channels_seen[my_chanid] = True
+            chan._set_transport(self)
+            chan._set_window(
+                self.default_window_size, self.default_max_packet_size
+            )
+            chan._set_remote_channel(
+                chanid, initial_window_size, max_packet_size
+            )
+        finally:
+            self.lock.release()
+        m = Message()
+        m.add_byte(cMSG_CHANNEL_OPEN_SUCCESS)
+        m.add_int(chanid)
+        m.add_int(my_chanid)
+        m.add_int(self.default_window_size)
+        m.add_int(self.default_max_packet_size)
+        self._send_message(m)
+        self._log(
+            DEBUG, "Secsh channel {:d} ({}) opened.".format(my_chanid, kind)
+        )
+        if kind == "auth-agent@openssh.com":
+            self._forward_agent_handler(chan)
+        elif kind == "x11":
+            self._x11_handler(chan, (origin_addr, origin_port))
+        elif kind == "forwarded-tcpip":
+            chan.origin_addr = (origin_addr, origin_port)
+            self._tcp_handler(
+                chan, (origin_addr, origin_port), (server_addr, server_port)
+            )
+        else:
+            self._queue_incoming_channel(chan)
+
+    def _parse_debug(self, m):
+        print("_parse_debug")
+        m.get_boolean()  # always_display
+        msg = m.get_string()
+        m.get_string()  # language
+        self._log(DEBUG, "Debug msg: {}".format(util.safe_string(msg)))
+
+    def _get_subsystem_handler(self, name):
+        print("_get_subsystem_handler")
+        try:
+            self.lock.acquire()
+            if name not in self.subsystem_table:
+                return None, [], {}
+            return self.subsystem_table[name]
+        finally:
+            self.lock.release()
+
+    _handler_table = {
+        MSG_EXT_INFO: _parse_ext_info,
+        MSG_NEWKEYS: _parse_newkeys,
+        MSG_GLOBAL_REQUEST: _parse_global_request,
+        MSG_REQUEST_SUCCESS: _parse_request_success,
+        MSG_REQUEST_FAILURE: _parse_request_failure,
+        MSG_CHANNEL_OPEN_SUCCESS: _parse_channel_open_success,
+        MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure,
+        MSG_CHANNEL_OPEN: _parse_channel_open,
+        MSG_KEXINIT: _negotiate_keys,
+    }
+
+    _channel_handler_table = {
+        MSG_CHANNEL_SUCCESS: Channel._request_success,
+        MSG_CHANNEL_FAILURE: Channel._request_failed,
+        MSG_CHANNEL_DATA: Channel._feed,
+        MSG_CHANNEL_EXTENDED_DATA: Channel._feed_extended,
+        MSG_CHANNEL_WINDOW_ADJUST: Channel._window_adjust,
+        MSG_CHANNEL_REQUEST: Channel._handle_request,
+        MSG_CHANNEL_EOF: Channel._handle_eof,
+        MSG_CHANNEL_CLOSE: Channel._handle_close,
+    }
+
+
+# TODO 4.0: drop this, we barely use it ourselves, it badly replicates the
+# Transport-internal algorithm management, AND does so in a way which doesn't
+# honor newer things like disabled_algorithms!
+class SecurityOptions:
+    """
+    Simple object containing the security preferences of an ssh transport.
+    These are tuples of acceptable ciphers, digests, key types, and key
+    exchange algorithms, listed in order of preference.
+
+    Changing the contents and/or order of these fields affects the underlying
+    `.Transport` (but only if you change them before starting the session).
+    If you try to add an algorithm that paramiko doesn't recognize,
+    ``ValueError`` will be raised.  If you try to assign something besides a
+    tuple to one of the fields, ``TypeError`` will be raised.
+    """
+
+    __slots__ = "_transport"
+
+    def __init__(self, transport):
+        self._transport = transport
+
+    def __repr__(self):
+        """
+        Returns a string representation of this object, for debugging.
+        """
+        return "<paramiko.SecurityOptions for {!r}>".format(self._transport)
+
+    def _set(self, name, orig, x):
+        if type(x) is list:
+            x = tuple(x)
+        if type(x) is not tuple:
+            raise TypeError("expected tuple or list")
+        possible = list(getattr(self._transport, orig).keys())
+        forbidden = [n for n in x if n not in possible]
+        if len(forbidden) > 0:
+            raise ValueError("unknown cipher")
+        setattr(self._transport, name, x)
+
+    @property
+    def ciphers(self):
+        """Symmetric encryption ciphers"""
+        return self._transport._preferred_ciphers
+
+    @ciphers.setter
+    def ciphers(self, x):
+        self._set("_preferred_ciphers", "_cipher_info", x)
+
+    @property
+    def digests(self):
+        """Digest (one-way hash) algorithms"""
+        return self._transport._preferred_macs
+
+    @digests.setter
+    def digests(self, x):
+        self._set("_preferred_macs", "_mac_info", x)
+
+    @property
+    def key_types(self):
+        """Public-key algorithms"""
+        return self._transport._preferred_keys
+
+    @key_types.setter
+    def key_types(self, x):
+        self._set("_preferred_keys", "_key_info", x)
+
+    @property
+    def kex(self):
+        """Key exchange algorithms"""
+        return self._transport._preferred_kex
+
+    @kex.setter
+    def kex(self, x):
+        self._set("_preferred_kex", "_kex_info", x)
+
+    @property
+    def compression(self):
+        """Compression algorithms"""
+        return self._transport._preferred_compression
+
+    @compression.setter
+    def compression(self, x):
+        self._set("_preferred_compression", "_compression_info", x)
+
+
+class ChannelMap:
+    def __init__(self):
+        # (id -> Channel)
+        self._map = weakref.WeakValueDictionary()
+        self._lock = threading.Lock()
+
+    def put(self, chanid, chan):
+        self._lock.acquire()
+        try:
+            self._map[chanid] = chan
+        finally:
+            self._lock.release()
+
+    def get(self, chanid):
+        self._lock.acquire()
+        try:
+            return self._map.get(chanid, None)
+        finally:
+            self._lock.release()
+
+    def delete(self, chanid):
+        self._lock.acquire()
+        try:
+            try:
+                del self._map[chanid]
+            except KeyError:
+                pass
+        finally:
+            self._lock.release()
+
+    def values(self):
+        self._lock.acquire()
+        try:
+            return list(self._map.values())
+        finally:
+            self._lock.release()
+
+    def __len__(self):
+        self._lock.acquire()
+        try:
+            return len(self._map)
+        finally:
+            self._lock.release()
diff --git a/paramiko/util.py b/paramiko/util.py
new file mode 100644
index 0000000..f1e33a5
--- /dev/null
+++ b/paramiko/util.py
@@ -0,0 +1,337 @@
+# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Useful functions used by the rest of paramiko.
+"""
+
+
+import sys
+import struct
+import traceback
+import threading
+import logging
+
+from paramiko.common import (
+    DEBUG,
+    zero_byte,
+    xffffffff,
+    max_byte,
+    byte_ord,
+    byte_chr,
+)
+from paramiko.config import SSHConfig
+
+
+def inflate_long(s, always_positive=False):
+    """turns a normalized byte string into a long-int
+    (adapted from Crypto.Util.number)"""
+    out = 0
+    negative = 0
+    if not always_positive and (len(s) > 0) and (byte_ord(s[0]) >= 0x80):
+        negative = 1
+    if len(s) % 4:
+        filler = zero_byte
+        if negative:
+            filler = max_byte
+        # never convert this to ``s +=`` because this is a string, not a number
+        # noinspection PyAugmentAssignment
+        s = filler * (4 - len(s) % 4) + s
+    for i in range(0, len(s), 4):
+        out = (out << 32) + struct.unpack(">I", s[i : i + 4])[0]
+    if negative:
+        out -= 1 << (8 * len(s))
+    return out
+
+
+def deflate_long(n, add_sign_padding=True):
+    """turns a long-int into a normalized byte string
+    (adapted from Crypto.Util.number)"""
+    # after much testing, this algorithm was deemed to be the fastest
+    s = bytes()
+    n = int(n)
+    while (n != 0) and (n != -1):
+        s = struct.pack(">I", n & xffffffff) + s
+        n >>= 32
+    # strip off leading zeros, FFs
+    for i in enumerate(s):
+        if (n == 0) and (i[1] != 0):
+            break
+        if (n == -1) and (i[1] != 0xFF):
+            break
+    else:
+        # degenerate case, n was either 0 or -1
+        i = (0,)
+        if n == 0:
+            s = zero_byte
+        else:
+            s = max_byte
+    s = s[i[0] :]
+    if add_sign_padding:
+        if (n == 0) and (byte_ord(s[0]) >= 0x80):
+            s = zero_byte + s
+        if (n == -1) and (byte_ord(s[0]) < 0x80):
+            s = max_byte + s
+    return s
+
+
+def format_binary(data, prefix=""):
+    x = 0
+    out = []
+    while len(data) > x + 16:
+        out.append(format_binary_line(data[x : x + 16]))
+        x += 16
+    if x < len(data):
+        out.append(format_binary_line(data[x:]))
+    return [prefix + line for line in out]
+
+
+def format_binary_line(data):
+    left = " ".join(["{:02X}".format(byte_ord(c)) for c in data])
+    right = "".join(
+        [".{:c}..".format(byte_ord(c))[(byte_ord(c) + 63) // 95] for c in data]
+    )
+    return "{:50s} {}".format(left, right)
+
+
+def safe_string(s):
+    out = b""
+    for c in s:
+        i = byte_ord(c)
+        if 32 <= i <= 127:
+            out += byte_chr(i)
+        else:
+            out += b("%{:02X}".format(i))
+    return out
+
+
+def bit_length(n):
+    try:
+        return n.bit_length()
+    except AttributeError:
+        norm = deflate_long(n, False)
+        hbyte = byte_ord(norm[0])
+        if hbyte == 0:
+            return 1
+        bitlen = len(norm) * 8
+        while not (hbyte & 0x80):
+            hbyte <<= 1
+            bitlen -= 1
+        return bitlen
+
+
+def tb_strings():
+    return "".join(traceback.format_exception(*sys.exc_info())).split("\n")
+
+
+def generate_key_bytes(hash_alg, salt, key, nbytes):
+    """
+    Given a password, passphrase, or other human-source key, scramble it
+    through a secure hash into some keyworthy bytes.  This specific algorithm
+    is used for encrypting/decrypting private key files.
+
+    :param function hash_alg: A function which creates a new hash object, such
+        as ``hashlib.sha256``.
+    :param salt: data to salt the hash with.
+    :type bytes salt: Hash salt bytes.
+    :param str key: human-entered password or passphrase.
+    :param int nbytes: number of bytes to generate.
+    :return: Key data, as `bytes`.
+    """
+    keydata = bytes()
+    digest = bytes()
+    if len(salt) > 8:
+        salt = salt[:8]
+    while nbytes > 0:
+        hash_obj = hash_alg()
+        if len(digest) > 0:
+            hash_obj.update(digest)
+        hash_obj.update(b(key))
+        hash_obj.update(salt)
+        digest = hash_obj.digest()
+        size = min(nbytes, len(digest))
+        keydata += digest[:size]
+        nbytes -= size
+    return keydata
+
+
+def load_host_keys(filename):
+    """
+    Read a file of known SSH host keys, in the format used by openssh, and
+    return a compound dict of ``hostname -> keytype ->`` `PKey
+    <paramiko.pkey.PKey>`. The hostname may be an IP address or DNS name.  The
+    keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``.
+
+    This type of file unfortunately doesn't exist on Windows, but on posix,
+    it will usually be stored in ``os.path.expanduser("~/.ssh/known_hosts")``.
+
+    Since 1.5.3, this is just a wrapper around `.HostKeys`.
+
+    :param str filename: name of the file to read host keys from
+    :return:
+        nested dict of `.PKey` objects, indexed by hostname and then keytype
+    """
+    from paramiko.hostkeys import HostKeys
+
+    return HostKeys(filename)
+
+
+def parse_ssh_config(file_obj):
+    """
+    Provided only as a backward-compatible wrapper around `.SSHConfig`.
+
+    .. deprecated:: 2.7
+        Use `SSHConfig.from_file` instead.
+    """
+    config = SSHConfig()
+    config.parse(file_obj)
+    return config
+
+
+def lookup_ssh_host_config(hostname, config):
+    """
+    Provided only as a backward-compatible wrapper around `.SSHConfig`.
+    """
+    return config.lookup(hostname)
+
+
+def mod_inverse(x, m):
+    # it's crazy how small Python can make this function.
+    u1, u2, u3 = 1, 0, m
+    v1, v2, v3 = 0, 1, x
+
+    while v3 > 0:
+        q = u3 // v3
+        u1, v1 = v1, u1 - v1 * q
+        u2, v2 = v2, u2 - v2 * q
+        u3, v3 = v3, u3 - v3 * q
+    if u2 < 0:
+        u2 += m
+    return u2
+
+
+_g_thread_data = threading.local()
+_g_thread_counter = 0
+_g_thread_lock = threading.Lock()
+
+
+def get_thread_id():
+    global _g_thread_data, _g_thread_counter, _g_thread_lock
+    try:
+        return _g_thread_data.id
+    except AttributeError:
+        with _g_thread_lock:
+            _g_thread_counter += 1
+            _g_thread_data.id = _g_thread_counter
+        return _g_thread_data.id
+
+
+def log_to_file(filename, level=DEBUG):
+    """send paramiko logs to a logfile,
+    if they're not already going somewhere"""
+    logger = logging.getLogger("paramiko")
+    if len(logger.handlers) > 0:
+        return
+    logger.setLevel(level)
+    f = open(filename, "a")
+    handler = logging.StreamHandler(f)
+    frm = "%(levelname)-.3s [%(asctime)s.%(msecs)03d] thr=%(_threadid)-3d"
+    frm += " %(name)s: %(message)s"
+    handler.setFormatter(logging.Formatter(frm, "%Y%m%d-%H:%M:%S"))
+    logger.addHandler(handler)
+
+
+# make only one filter object, so it doesn't get applied more than once
+class PFilter:
+    def filter(self, record):
+        record._threadid = get_thread_id()
+        return True
+
+
+_pfilter = PFilter()
+
+
+def get_logger(name):
+    logger = logging.getLogger(name)
+    logger.addFilter(_pfilter)
+    return logger
+
+
+def constant_time_bytes_eq(a, b):
+    if len(a) != len(b):
+        return False
+    res = 0
+    # noinspection PyUnresolvedReferences
+    for i in range(len(a)):  # noqa: F821
+        res |= byte_ord(a[i]) ^ byte_ord(b[i])
+    return res == 0
+
+
+class ClosingContextManager:
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.close()
+
+
+def clamp_value(minimum, val, maximum):
+    return max(minimum, min(val, maximum))
+
+
+def asbytes(s):
+    """
+    Coerce to bytes if possible or return unchanged.
+    """
+    try:
+        # Attempt to run through our version of b(), which does the Right Thing
+        # for unicode strings vs bytestrings, and raises TypeError if it's not
+        # one of those types.
+        return b(s)
+    except TypeError:
+        try:
+            # If it wasn't a string/byte/buffer-ish object, try calling an
+            # asbytes() method, which many of our internal classes implement.
+            return s.asbytes()
+        except AttributeError:
+            # Finally, just do nothing & assume this object is sufficiently
+            # byte-y or buffer-y that everything will work out (or that callers
+            # are capable of handling whatever it is.)
+            return s
+
+
+# TODO: clean this up / force callers to assume bytes OR unicode
+def b(s, encoding="utf8"):
+    """cast unicode or bytes to bytes"""
+    if isinstance(s, bytes):
+        return s
+    elif isinstance(s, str):
+        return s.encode(encoding)
+    else:
+        raise TypeError(f"Expected unicode or bytes, got {type(s)}")
+
+
+# TODO: clean this up / force callers to assume bytes OR unicode
+def u(s, encoding="utf8"):
+    """cast bytes or unicode to unicode"""
+    if isinstance(s, bytes):
+        return s.decode(encoding)
+    elif isinstance(s, str):
+        return s
+    else:
+        raise TypeError(f"Expected unicode or bytes, got {type(s)}")
diff --git a/paramiko/win_openssh.py b/paramiko/win_openssh.py
new file mode 100644
index 0000000..614b589
--- /dev/null
+++ b/paramiko/win_openssh.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2021 Lew Gordon <lew.gordon@genesys.com>
+# Copyright (C) 2022 Patrick Spendrin <ps_ml@gmx.de>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+import os.path
+import time
+
+PIPE_NAME = r"\\.\pipe\openssh-ssh-agent"
+
+
+def can_talk_to_agent():
+    # use os.listdir() instead of os.path.exists(), because os.path.exists()
+    # uses CreateFileW() API and the pipe cannot be reopen unless the server
+    # calls DisconnectNamedPipe().
+    dir_, name = os.path.split(PIPE_NAME)
+    name = name.lower()
+    return any(name == n.lower() for n in os.listdir(dir_))
+
+
+class OpenSSHAgentConnection:
+    def __init__(self):
+        while True:
+            try:
+                self._pipe = os.open(PIPE_NAME, os.O_RDWR | os.O_BINARY)
+            except OSError as e:
+                # retry when errno 22 which means that the server has not
+                # called DisconnectNamedPipe() yet.
+                if e.errno != 22:
+                    raise
+            else:
+                break
+            time.sleep(0.1)
+
+    def send(self, data):
+        return os.write(self._pipe, data)
+
+    def recv(self, n):
+        return os.read(self._pipe, n)
+
+    def close(self):
+        return os.close(self._pipe)
diff --git a/paramiko/win_pageant.py b/paramiko/win_pageant.py
new file mode 100644
index 0000000..c927de6
--- /dev/null
+++ b/paramiko/win_pageant.py
@@ -0,0 +1,138 @@
+# Copyright (C) 2005 John Arbash-Meinel <john@arbash-meinel.com>
+# Modified up by: Todd Whiteman <ToddW@ActiveState.com>
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+
+"""
+Functions for communicating with Pageant, the basic windows ssh agent program.
+"""
+
+import array
+import ctypes.wintypes
+import platform
+import struct
+from paramiko.common import zero_byte
+from paramiko.util import b
+
+import _thread as thread
+
+from . import _winapi
+
+
+_AGENT_COPYDATA_ID = 0x804E50BA
+_AGENT_MAX_MSGLEN = 8192
+# Note: The WM_COPYDATA value is pulled from win32con, as a workaround
+# so we do not have to import this huge library just for this one variable.
+win32con_WM_COPYDATA = 74
+
+
+def _get_pageant_window_object():
+    return ctypes.windll.user32.FindWindowA(b"Pageant", b"Pageant")
+
+
+def can_talk_to_agent():
+    """
+    Check to see if there is a "Pageant" agent we can talk to.
+
+    This checks both if we have the required libraries (win32all or ctypes)
+    and if there is a Pageant currently running.
+    """
+    return bool(_get_pageant_window_object())
+
+
+if platform.architecture()[0] == "64bit":
+    ULONG_PTR = ctypes.c_uint64
+else:
+    ULONG_PTR = ctypes.c_uint32
+
+
+class COPYDATASTRUCT(ctypes.Structure):
+    """
+    ctypes implementation of
+    http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010%28v=vs.85%29.aspx
+    """
+
+    _fields_ = [
+        ("num_data", ULONG_PTR),
+        ("data_size", ctypes.wintypes.DWORD),
+        ("data_loc", ctypes.c_void_p),
+    ]
+
+
+def _query_pageant(msg):
+    """
+    Communication with the Pageant process is done through a shared
+    memory-mapped file.
+    """
+    hwnd = _get_pageant_window_object()
+    if not hwnd:
+        # Raise a failure to connect exception, pageant isn't running anymore!
+        return None
+
+    # create a name for the mmap
+    map_name = f"PageantRequest{thread.get_ident():08x}"
+
+    pymap = _winapi.MemoryMap(
+        map_name, _AGENT_MAX_MSGLEN, _winapi.get_security_attributes_for_user()
+    )
+    with pymap:
+        pymap.write(msg)
+        # Create an array buffer containing the mapped filename
+        char_buffer = array.array("b", b(map_name) + zero_byte)  # noqa
+        char_buffer_address, char_buffer_size = char_buffer.buffer_info()
+        # Create a string to use for the SendMessage function call
+        cds = COPYDATASTRUCT(
+            _AGENT_COPYDATA_ID, char_buffer_size, char_buffer_address
+        )
+
+        response = ctypes.windll.user32.SendMessageA(
+            hwnd, win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds)
+        )
+
+        if response > 0:
+            pymap.seek(0)
+            datalen = pymap.read(4)
+            retlen = struct.unpack(">I", datalen)[0]
+            return datalen + pymap.read(retlen)
+        return None
+
+
+class PageantConnection:
+    """
+    Mock "connection" to an agent which roughly approximates the behavior of
+    a unix local-domain socket (as used by Agent).  Requests are sent to the
+    pageant daemon via special Windows magick, and responses are buffered back
+    for subsequent reads.
+    """
+
+    def __init__(self):
+        self._response = None
+
+    def send(self, data):
+        self._response = _query_pageant(data)
+
+    def recv(self, n):
+        if self._response is None:
+            return ""
+        ret = self._response[:n]
+        self._response = self._response[n:]
+        if self._response == "":
+            self._response = None
+        return ret
+
+    def close(self):
+        pass
diff --git a/ssh-keys/hello.txt b/ssh-keys/hello.txt
new file mode 100644
index 0000000..6751cc9
--- /dev/null
+++ b/ssh-keys/hello.txt
@@ -0,0 +1,2 @@
+mes1:  paramiko.Message(b'2\x00\x00\x00\tparallels\x00\x00\x00\x0essh-connection\x00\x00\x00\tpublickey\x01\x00\x00\x00\x0crsa-sha2-512\x00\x00\x01\x97\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x81\x00\xdb~\x08\xebLX\n\x93.jw\x07u\xb4<=\x83U\xe1V\x14\xf2w\xcb\x1e\xa7OWf\xac\x88\xb6\xa7\xd4\xf5\xd1\xe2}t\xcfT\xa9\xe4\xec\x99\xe8\x97i\xe1\xb8\x95\xc9B\xfb;\x86\xb3\xebPL\x11\x18n\xefN\xfd\x1a;\xc7\xdeM\x1b\xf3y>\x02l\x90\x18\xc7\xbe4\xf7`\xcd\'\xc8\x88\xa9V\xdd\x0b3X\t\xe7\xcd2O\xfaN\x88\x03\xa1\xc9dY\x89xojF\x93\xf1\xad\x85\x03#\xebV\x97\x19\x05\xeaw*\xcdD\x81\xa0\x84\x1a\xfd\x99\x0b\xb9\xd2.\xbf\x94\xf1\x10A`\x14Ssvr6\x0c\x03\xbb\x98q\xc4&=\xd4S\xd3\xb9\xa4\x05\xfe\xddAY\x0eQ\xf8g\xd0\xd1|V\x13%\x91\xe2\xb9\xdd\xf2\xd6\xf6\x11\xb6\x9a\x11\x04\xfa\xa4+$\x94O\x03\x89\x9e\xedJw \x87\xc6-s\xe2\xde\x87\x80\x01LH\xd0Hm\x051Y\x19\xcaPX\x161\x88\xc65\x9c\xe8\xf5w\xe0\xd2C,\x9d\x9c{\x91\xc6]\x94\xc8\xd7\x93\x16Y9u^I\xbe\x8c\xda\xf5m:0\xcb\xae\xf8\x1a\xa1\xb7\xbe\xdf\r\xc3\x82V\xb0\xf3\xd6\xe5\xbf\xf6\xbc\x1d\xed\x17P\xe3M\x84G\xb2\xf6\xea~\x02f\xcd\x18\xf1\xa84E\xd3KW\xe8\x17\x98Q\x14y\xf6\xb2!\x08\x95\x06K\xb3\xa0\xc2.\xe6\xb87D\xe0A\xb6\xdb\x19\x10>T0\x91W\xfc\xf8\xa5\xa9\xfa\xd8g\xb1\x1d\xe6UKs\x7fhX^\x9b\x92\x06>\x0c(\x12\x19ZN\x8e\x14BZ\x99\x1b\xed\x141\x8f*\x9d$\x19\xa5X\xc1\x914z\xbe<\xd1\x00\x00\x01\x94\x00\x00\x00\x0crsa-sha2-512\x00\x00\x01\x80\xca\x7f593\x85\xecz\x0b\xe2I\x99\xbe#w6\x87\xba~t\x92\x9c`7\xf1E\x1a\xf6\x83\xb6\x0b\xfb\xa8\x00]+\xfc\xd5\xad\xc8<H\x90@\xb5\xb7Z{\xeas\x9d\x01\x13G\x0f_D\x88\xd4\xb9+v\x91\xd0w\xf1\xdcq\x7f\x8a\x1c\x99\x16\xa3SG)\xfd\xd3\xbd\x057\xf3\\\x89\xd2\xcfJe\xbcn\x93\xae\x06Q\x80l\x85\xac1\xe4[\xab\xe2\xe7.\xb9)\x15\x9d\xed\x82|`\x8b\xd1\x14N\x18\xaadKB\xec\xc7\x0c2\xb7\x87\x83\xb7\x95\x1a4\x1e\xa15\x19\xa6\xc2b\n\x0e`\x11\x9e\x89q\xeaV\x97\xf8;\x94K"\xb3\x0f\xa7@\xb6\xd5\x80\x17\xcf_6\xc5\xae\x19\x06vf\xa0\\\xf7\r6\xfc\x80\xb7$j\xa6\x07k\xe3\xbc\x12&\xe4Qg\x06.\xab\xb3\xac\x93\xe1K\xfc3\xcd\xd4\xcf\xf5vt\xd0\x95\xfd\xef\x02%\xcc\xc6\xce\xdc#6Nn\ta\x95\xa0\xae\x1a\xa2\xa4\xda\xd3\xd5\xf2\xb5\xddI\xbb\xa24\xb4\xf4\xae!:\x9f\xb7\xbc\xedJfL\xedZI\xd1\xa0\xc4\xadfG\xff\xbb\xf6c\x10\x0b\x9aoi\xb0\xddS\x898\x02\xa2\x7f\x83j{0<\xa2O\xb9Ar^\x93f{\x16\xbby\xa0\xc5\xcf\xcaU\xad\x11\xf5\r\xec\xd9\xd0\xbb$\xf4G\xb5\xcaq\x00\x89c\x0b\xa4\x84\xbf|[\x001\xd9\xfd\xdb^[\x8a@\xcc\xba\xb3\xe4\xd1:\x05\xf3\x8b}\xe5\xb9Y\x9a\xf2\x8b\x0f\xa2\x8d\x15\x94Mr\x0f\x01<2\x95`\xf9\x80\x88:4\xdf\xa6}Q\xdd\x0bS\x9aEU6\xdb\x14i\xc6\xc4\x04')
+mes2:  paramiko.Message(b'2\x00\x00\x00\tparallels\x00\x00\x00\x0essh-connection\x00\x00\x00\tpublickey\x01\x00\x00\x00\x0crsa-sha2-512\x00\x00\x01\x97\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x81\x00\xdb~\x08\xebLX\n\x93.jw\x07u\xb4<=\x83U\xe1V\x14\xf2w\xcb\x1e\xa7OWf\xac\x88\xb6\xa7\xd4\xf5\xd1\xe2}t\xcfT\xa9\xe4\xec\x99\xe8\x97i\xe1\xb8\x95\xc9B\xfb;\x86\xb3\xebPL\x11\x18n\xefN\xfd\x1a;\xc7\xdeM\x1b\xf3y>\x02l\x90\x18\xc7\xbe4\xf7`\xcd\'\xc8\x88\xa9V\xdd\x0b3X\t\xe7\xcd2O\xfaN\x88\x03\xa1\xc9dY\x89xojF\x93\xf1\xad\x85\x03#\xebV\x97\x19\x05\xeaw*\xcdD\x81\xa0\x84\x1a\xfd\x99\x0b\xb9\xd2.\xbf\x94\xf1\x10A`\x14Ssvr6\x0c\x03\xbb\x98q\xc4&=\xd4S\xd3\xb9\xa4\x05\xfe\xddAY\x0eQ\xf8g\xd0\xd1|V\x13%\x91\xe2\xb9\xdd\xf2\xd6\xf6\x11\xb6\x9a\x11\x04\xfa\xa4+$\x94O\x03\x89\x9e\xedJw \x87\xc6-s\xe2\xde\x87\x80\x01LH\xd0Hm\x051Y\x19\xcaPX\x161\x88\xc65\x9c\xe8\xf5w\xe0\xd2C,\x9d\x9c{\x91\xc6]\x94\xc8\xd7\x93\x16Y9u^I\xbe\x8c\xda\xf5m:0\xcb\xae\xf8\x1a\xa1\xb7\xbe\xdf\r\xc3\x82V\xb0\xf3\xd6\xe5\xbf\xf6\xbc\x1d\xed\x17P\xe3M\x84G\xb2\xf6\xea~\x02f\xcd\x18\xf1\xa84E\xd3KW\xe8\x17\x98Q\x14y\xf6\xb2!\x08\x95\x06K\xb3\xa0\xc2.\xe6\xb87D\xe0A\xb6\xdb\x19\x10>T0\x91W\xfc\xf8\xa5\xa9\xfa\xd8g\xb1\x1d\xe6UKs\x7fhX^\x9b\x92\x06>\x0c(\x12\x19ZN\x8e\x14BZ\x99\x1b\xed\x141\x8f*\x9d$\x19\xa5X\xc1\x914z\xbe<\xd1\x00\x00\x01\x94\x00\x00\x00\x0crsa-sha2-512\x00\x00\x01\x80\xca\x7f593\x85\xecz\x0b\xe2I\x99\xbe#w6\x87\xba~t\x92\x9c`7\xf1E\x1a\xf6\x83\xb6\x0b\xfb\xa8\x00]+\xfc\xd5\xad\xc8<H\x90@\xb5\xb7Z{\xeas\x9d\x01\x13G\x0f_D\x88\xd4\xb9+v\x91\xd0w\xf1\xdcq\x7f\x8a\x1c\x99\x16\xa3SG)\xfd\xd3\xbd\x057\xf3\\\x89\xd2\xcfJe\xbcn\x93\xae\x06Q\x80l\x85\xac1\xe4[\xab\xe2\xe7.\xb9)\x15\x9d\xed\x82|`\x8b\xd1\x14N\x18\xaadKB\xec\xc7\x0c2\xb7\x87\x83\xb7\x95\x1a4\x1e\xa15\x19\xa6\xc2b\n\x0e`\x11\x9e\x89q\xeaV\x97\xf8;\x94K"\xb3\x0f\xa7@\xb6\xd5\x80\x17\xcf_6\xc5\xae\x19\x06vf\xa0\\\xf7\r6\xfc\x80\xb7$j\xa6\x07k\xe3\xbc\x12&\xe4Qg\x06.\xab\xb3\xac\x93\xe1K\xfc3\xcd\xd4\xcf\xf5vt\xd0\x95\xfd\xef\x02%\xcc\xc6\xce\xdc#6Nn\ta\x95\xa0\xae\x1a\xa2\xa4\xda\xd3\xd5\xf2\xb5\xddI\xbb\xa24\xb4\xf4\xae!:\x9f\xb7\xbc\xedJfL\xedZI\xd1\xa0\xc4\xadfG\xff\xbb\xf6c\x10\x0b\x9aoi\xb0\xddS\x898\x02\xa2\x7f\x83j{0<\xa2O\xb9Ar^\x93f{\x16\xbby\xa0\xc5\xcf\xcaU\xad\x11\xf5\r\xec\xd9\xd0\xbb$\xf4G\xb5\xcaq\x00\x89c\x0b\xa4\x84\xbf|[\x001\xd9\xfd\xdb^[\x8a@\xcc\xba\xb3\xe4\xd1:\x05\xf3\x8b}\xe5\xb9Y\x9a\xf2\x8b\x0f\xa2\x8d\x15\x94Mr\x0f\x01<2\x95`\xf9\x80\x88:4\xdf\xa6}Q\xdd\x0bS\x9aEU6\xdb\x14i\xc6\xc4\x04')
\ No newline at end of file
diff --git a/ssh-keys/hello2.txt b/ssh-keys/hello2.txt
new file mode 100644
index 0000000..e69de29
diff --git a/ssh-keys/test b/ssh-keys/test
new file mode 100644
index 0000000..381b68f
--- /dev/null
+++ b/ssh-keys/test
@@ -0,0 +1,39 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+bAECAw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/ssh-keys/test.pub b/ssh-keys/test.pub
new file mode 100644
index 0000000..3426f41
--- /dev/null
+++ b/ssh-keys/test.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDbfgjrTFgKky5qdwd1tDw9g1XhVhTyd8sep09XZqyItqfU9dHifXTPVKnk7Jnol2nhuJXJQvs7hrPrUEwRGG7vTv0aO8feTRvzeT4CbJAYx74092DNJ8iIqVbdCzNYCefNMk/6TogDoclkWYl4b2pGk/GthQMj61aXGQXqdyrNRIGghBr9mQu50i6/lPEQQWAUU3N2cjYMA7uYccQmPdRT07mkBf7dQVkOUfhn0NF8VhMlkeK53fLW9hG2mhEE+qQrJJRPA4me7Up3IIfGLXPi3oeAAUxI0EhtBTFZGcpQWBYxiMY1nOj1d+DSQyydnHuRxl2UyNeTFlk5dV5Jvoza9W06MMuu+Bqht77fDcOCVrDz1uW/9rwd7RdQ402ER7L26n4CZs0Y8ag0RdNLV+gXmFEUefayIQiVBkuzoMIu5rg3ROBBttsZED5UMJFX/PilqfrYZ7Ed5lVLc39oWF6bkgY+DCgSGVpOjhRCWpkb7RQxjyqdJBmlWMGRNHq+PNE= jorgemachado@MacBook-Pro-de-Jorge.local
diff --git a/ssh-keys/yeeee b/ssh-keys/yeeee
new file mode 100644
index 0000000..ab87036
--- /dev/null
+++ b/ssh-keys/yeeee
@@ -0,0 +1,39 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+bAECAw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/ssh-keys/yeeee.pub b/ssh-keys/yeeee.pub
new file mode 100644
index 0000000..a21eee8
--- /dev/null
+++ b/ssh-keys/yeeee.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDvDZoQ4jLcJVJFQ+1bU4yYkNaOFF8HxiJHN8fAKpwiQnk1bKUa/ucfVbjm8MAzli1ieK11Hvp4p39fUYfWJOXR+OB0Ci5Yd5qs1nwq9NRUKt9c1PgwCcBF1iMywSDLZNDecoQDaycZ9+8BODbj/Wx9vGzmPO61NxrTFSmqWBbUASBu9D6w9rZGuVMRKz+V1syjjHggj8D/NMoDoQqTq0UpZ9kxR4oZ78R/Ka79xXoHd0ZbkzmiGD3kVGFNK6r1orFa/O4HE0k9zU2LAuoykPZs9x2s5XvqHRrkbxp+tqSRO//Ejn/hYaijZoXbcso02VMEmIDLaMuHBs4a9TEUVQJ8vfnYcB+O7fuXZo0+UffX/rFftsMJoww/JFPYKAvjgNDhpE8GpvC1QnmS9m5yC4e55SfsE4FceY+GDuV9EmBtLTpgEIL7FJDlF3JxQfXfegBdqHee2b12arvalM3rkr+ZkNEljNASsydOkns6CIZ2lYBGh6dHS99qyQYDojN9Ih0= jorgemachado@MacBook-Pro-de-Jorge.local
-- 
GitLab