File feature-fix-update-ssh-stack.patch of Package erlang.42826

Index: otp-OTP-23.3.4.19/lib/ssh/doc/html/.gitignore
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/doc/html/.gitignore
@@ -0,0 +1 @@
+*
Index: otp-OTP-23.3.4.19/lib/ssh/doc/specs/.gitignore
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
Index: otp-OTP-23.3.4.19/lib/ssh/doc/src/notes.xml
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/doc/src/notes.xml
+++ otp-OTP-23.3.4.19/lib/ssh/doc/src/notes.xml
@@ -4,7 +4,7 @@
 <chapter>
   <header>
     <copyright>
-      <year>2004</year><year>2020</year>
+      <year>2004</year><year>2024</year>
       <holder>Ericsson AB. All Rights Reserved.</holder>
     </copyright>
     <legalnotice>
@@ -30,6 +30,1184 @@
     <file>notes.xml</file>
   </header>
 
+<section><title>Ssh 5.1.4.13</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>With this change user space buffers are used to limit
+	    ssh hello message size instead of kernel buffers</p>
+          <p>
+	    Own Id: OTP-19839 Aux Id: ERIERL-1273, PR-10350 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.12</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Option max_handles can be configured for sshd running
+	    SFTP. The positive integer value limits amount of file
+	    handles opened for a connection (by default 4096 is
+	    used).</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19701 Aux Id: CVE-2025-48041, PR-10157 </p>
+        </item>
+        <item>
+	    <p>Avoid decoding KEX messages providing too many
+	    algorithms. This change does not introduce new limitation
+	    but assures it is enforced earlier in processing chain.
+	    Adjustments in error logging during handshake.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19741 Aux Id: CVE-2025-48040, PR-10162 </p>
+        </item>
+        <item>
+	    <p>A new 'max_path' option is now available in the sshd
+	    configuration, allowing administrators to set the maximum
+	    allowable path length. By default, this value is set to
+	    4096 characters.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19742 Aux Id: CVE-2025-48039, PR-10155 </p>
+        </item>
+        <item>
+	    <p>Reject file handles exceeding size specified in RFCs
+	    (256 bytes).</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-19748 Aux Id: CVE-2025-48038, PR-10156 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.11</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Fix file handle id generation.</p>
+          <p>
+	    Own Id: OTP-19691 Aux Id: PR-10003 </p>
+        </item>
+        <item>
+	    <p>Fixes a badmatch error, when SFTP operation cannot be
+	    processed due to channel closed in parallel.</p>
+          <p>
+	    Own Id: OTP-19707 Aux Id: GH-9655, PR-10035, PR-10036 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.10</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Various channel closing robustness improvements. Avoid
+	    crashes when channel handling process closes channel and
+	    immediately exits. Avoid breaking the protocol by sending
+	    duplicated channel-close messages. Cleanup channels which
+	    timeout during closing procedure.</p>
+          <p>
+	    Own Id: OTP-19634 Aux Id: GH-9102, PR-9103 </p>
+        </item>
+        <item>
+	    <p>Improved interoperability with clients acting as
+	    Paramiko.</p>
+          <p>
+	    Own Id: OTP-19637 Aux Id: GH-6463, PR-9838 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.9</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Fix KEX strict implementation according to
+	    draft-miller-sshm-strict-kex-01 document.</p>
+          <p>
+	    Own Id: OTP-19625 Aux Id: CVE-2025-46712 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.8</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Reception of wrong Unicode does not cause unnecessary
+	    processing. US-ASCII fields are not decoded as
+	    Unicode.</p>
+          <p>
+	    Own Id: OTP-19582 Aux Id: PR-9679 </p>
+        </item>
+        <item>
+	    <p>SSH daemon disconnects upon receiving connection
+	    protocol message for unauthenticated used.</p>
+	    <p>Thanks to Fabian Bäumer, Marcel Maehren, Marcus
+	    Brinkmann, Nurullah Erinola, Jörg Schwenk (Ruhr
+	    University Bochum).</p>
+          <p>
+	    Own Id: OTP-19595 Aux Id: CVE-2025-32433 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.7</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Reception of malicious KEX init message does not
+	    result with ssh daemon excessive memory usage.</p>
+          <p>
+	    Own Id: OTP-19543 Aux Id: CVE-2025-30211 </p>
+        </item>
+        <item>
+	    <p>Call to ssh:daemon_replace_options does not crash when
+	    argument is not a valid daemon ref.</p>
+          <p>
+	    Own Id: OTP-19559 Aux Id: GH-9554, PR-9545 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.6</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>SFTP packets exceeding max packet size are not
+	    processed and dropped.</p>
+          <p>
+	    Own Id: OTP-19466 Aux Id: ERIERL-1173, CVE-2025-26618 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.5</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>With this change, type specs for
+	    ssh:connection_info/1,2 functions are fixed so they
+	    include {error, term()} return value.</p>
+          <p>
+	    Own Id: OTP-19388 Aux Id: ERIERL-1165, PR-9161 </p>
+        </item>
+        <item>
+	    <p>With this change, ssh client accepts a banner sent
+	    during processing keyboard interactive user
+	    authentication.</p>
+          <p>
+	    Own Id: OTP-19392 Aux Id: PR-9139, GH-9065 </p>
+        </item>
+        <item>
+	    <p> With this change, large sftp transfers does not hang.
+	    Redundant window adjustment are not requested. </p>
+          <p>
+	    Own Id: OTP-19435 Aux Id: PR-9309 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>With this change, ssh connection does not crash upon
+	    receiving exit-signal message for an already terminated
+	    channel.</p>
+          <p>
+	    Own Id: OTP-19326 Aux Id: PR-8995, GH-8929 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, a race condition is removed from ssh
+	    client connection setup procedure.</p>
+          <p>
+	    Own Id: OTP-19124 Aux Id: GH-7550, PR-8766 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, ssh:connect is not affected by presence
+	    of EXIT message in queue.</p>
+          <p>
+	    Own Id: OTP-19246 Aux Id: GH-8223, PR-8854 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, ssh appends {active, false} option
+	    after socket options received from user - so that false
+	    value is always used.</p>
+          <p>
+	    Own Id: OTP-19247 Aux Id: PR-8226 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, ssh daemon started with TCP port number
+	    argument will re-try to obtain listen socket before
+	    returning error to user.</p>
+          <p>
+	    Own Id: OTP-19170 Aux Id: GH-7746 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, robustness is improved by monitoring
+	    connection handler process before casting socket control
+	    notification.</p>
+          <p>
+	    Own Id: OTP-19173 Aux Id: PR-8310 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, ssh client will automatically adjust
+	    transfer window size for commands executed remotely over
+	    SSH.</p>
+          <p>
+	    Own Id: OTP-19057 Aux Id: PR-8345, GH-7483 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, race condition between connection
+	    closing and automatic window adjustment is fixed.</p>
+          <p>
+	    Own Id: OTP-19109 Aux Id: PR-8345 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, owner and group file attributes
+	    decoding is fixed and results with value of integer type.</p>
+          <p>
+	    Own Id: OTP-19013 Aux Id: GH-7897, PR-8220 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, acceptor_sup is not started for ssh
+	    client as it is not needed in that role.</p>
+          <p>
+	    Own Id: OTP-18974 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, more secure algorithms are preferred by
+	    ssh and documentation is updated to reflect that.</p>
+          <p>
+	    Own Id: OTP-18986 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, KEX strict terminal message is emitted
+	    with debug verbosity.</p>
+          <p>
+	    Own Id: OTP-19002 Aux Id: ERIERL-1041 </p>
+        </item>
+        <item>
+          <p>
+	    Fix reading of password for ssh client when in
+	    <c>user_interactive</c> mode.</p>
+          <p>
+	    Own Id: OTP-19007 Aux Id: ERIERL-1049 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, Curve25519 and Curve448 KEX methods
+	    become most preferred (related to RFC8731).</p>
+          <p>
+	    Own Id: OTP-18964 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change (being response to CVE-2023-48795), ssh
+	    can negotiate "strict KEX" OpenSSH extension with peers
+	    supporting it; also 'chacha20-poly1305@openssh.com'
+	    algorithm becomes a less preferred cipher.</p>
+          <p>
+	    If strict KEX availability cannot be ensured on both
+	    connection sides, affected encryption modes(CHACHA and
+	    CBC) can be disabled with standard ssh configuration.
+	    This will provide protection against vulnerability, but
+	    at a cost of affecting interoperability. See <seeguide
+	    marker="configure_algos">Configuring algorithms in
+	    SSH</seeguide>.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-18897</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Replaced unintentional Erlang Public License 1.1 headers
+	    in some files with the intended Apache License 2.0
+	    header.</p>
+          <p>
+	    Own Id: OTP-18815 Aux Id: PR-7780 </p>
+        </item>
+        <item>
+          <p>
+	    Avoid outputting ansi escape sequences to dumb ssh
+	    clients.</p>
+          <p>
+	    Own Id: OTP-18861 Aux Id: PR-7627 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, connection handler does not execute
+	    socket operations until it becomes socket owner.
+	    Previously errors could occur if connection handler tried
+	    to work with socket whose owner exited.</p>
+          <p>
+	    Own Id: OTP-18869 Aux Id: PR-7849,GH-7571 </p>
+        </item>
+      </list>
+    </section>
+
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    With this change, reverse search works with ssh shell and
+	    non dumb terminals.</p>
+          <p>
+	    Own Id: OTP-18730 Aux Id: PR-7499 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.0.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Added multiline editing support to ssh clients connected
+	    through OTP ssh daemon.</p>
+          <p>
+	    Own Id: OTP-18653 Aux Id: PR-7242 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 5.0</title>
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    The ssh_cli has been updated to work with the changes
+	    introduced in the new Erlang shell implementation.</p>
+          <p>
+	    Own Id: OTP-18231 Aux Id: OTP-17932 PR-6144 </p>
+        </item>
+        <item>
+          <p>
+	    Typing <c>Ctrl+L</c> in a shell now clears the screen and
+	    redraws the current line instead of only redrawing the
+	    current line. To only redraw the current line, you must
+	    now type <c>Alt+L</c>. This brings the behaviour of
+	    <c>Ctrl+L</c> closer to how bash and other shells work.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-18285 Aux Id: PR-6262 </p>
+        </item>
+        <item>
+          <p>
+	    Deprecates <c>dbg:stop_clear/0</c> because it is simply a
+	    function alias to <c>dbg:stop/0</c></p>
+          <p>
+	    Own Id: OTP-18478 Aux Id: GH-6903 </p>
+        </item>
+        <item>
+	    <p> The implementation has been fixed to use
+	    <c>proc_lib:init_fail/2,3</c> where appropriate, instead
+	    of <c>proc_lib:init_ack/1,2</c>. </p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-18490 Aux Id: OTP-18471, GH-6339, PR-6843 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.15.3.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, Curve25519 and Curve448 KEX methods
+	    become most preferred (related to RFC8731).</p>
+          <p>
+	    Own Id: OTP-18964 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.15.3.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, connection handler does not execute
+	    socket operations until it becomes socket owner.
+	    Previously errors could occur if connection handler tried
+	    to work with socket whose owner exited.</p>
+          <p>
+	    Own Id: OTP-18869 Aux Id: PR-7849,GH-7571 </p>
+        </item>
+        <item>
+          <p>
+	    With this change (being response to CVE-2023-48795), ssh
+	    can negotiate "strict KEX" OpenSSH extension with peers
+	    supporting it; also 'chacha20-poly1305@openssh.com'
+	    algorithm becomes a less preferred cipher.</p>
+          <p>
+	    If strict KEX availability cannot be ensured on both
+	    connection sides, affected encryption modes(CHACHA and
+	    CBC) can be disabled with standard ssh configuration.
+	    This will provide protection against vulnerability, but
+	    at a cost of affecting interoperability. See <seeguide
+	    marker="configure_algos">Configuring algorithms in
+	    SSH</seeguide>.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-18897</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.15.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, PKCS8 formatted private key file is
+	    properly decoded and SSH daemon with such key can be
+	    started.</p>
+          <p>
+	    Own Id: OTP-18446 Aux Id: GH-6475 </p>
+        </item>
+      </list>
+    </section>
+
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    Replace size/1 with either tuple_size/1 or byte_size/1</p>
+          <p>
+	    The <c>size/1</c> BIF is not optimized by the JIT, and
+	    its use can result in worse types for Dialyzer.</p>
+          <p>
+	    When one knows that the value being tested must be a
+	    tuple, <c>tuple_size/1</c> should always be preferred.</p>
+          <p>
+	    When one knows that the value being tested must be a
+	    binary, <c>byte_size/1</c> should be preferred. However,
+	    <c>byte_size/1</c> also accepts a bitstring (rounding up
+	    size to a whole number of bytes), so one must make sure
+	    that the call to <c>byte_size/</c> is preceded by a call
+	    to <c>is_binary/1</c> to ensure that bitstrings are
+	    rejected. Note that the compiler removes redundant calls
+	    to <c>is_binary/1</c>, so if one is not sure whether
+	    previous code had made sure that the argument is a
+	    binary, it does not harm to add an <c>is_binary/1</c>
+	    test immediately before the call to <c>byte_size/1</c>.</p>
+          <p>
+	    Own Id: OTP-18432 Aux Id:
+	    GH-6672,PR-6793,PR-6784,PR-6787,PR-6785,PR-6682,PR-6800,PR-6797,PR-6798,PR-6799,PR-6796,PR-6813,PR-6671,PR-6673,PR-6684,PR-6694,GH-6677,PR-6696,PR-6670,PR-6674 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.15.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, ssh application does not crash when
+	    formatting some of info reports for unsuccessful
+	    connections.</p>
+          <p>
+	    Own Id: OTP-18386 Aux Id: PR-6611 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, ssh does not log extensively long
+	    messages.</p>
+          <p>
+	    Own Id: OTP-18417 Aux Id: DAFH-1349,ERIERL-888,IA18357 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.15.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    graceful shutdown of ssh_conection_handler when
+	    connection is closed by peer</p>
+          <p>
+	    Own Id: OTP-18326 Aux Id: ERIERL-865 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.15</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Handling rare race condition at channel close.</p>
+          <p>
+	    Own Id: OTP-18220 Aux Id: ERIERL-666, ERIERL-661 </p>
+        </item>
+      </list>
+    </section>
+
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    New ssh option <c>no_auth_needed</c> to skip the ssh
+	    authentication. Use with caution!</p>
+          <p>
+	    Own Id: OTP-18134 Aux Id: GH-6021 </p>
+        </item>
+        <item>
+          <p>
+	    This change fixes dialyzer warnings generated for
+	    inets/httpd examples (includes needed adjustment of spec
+	    for ssh_sftp module).</p>
+          <p>
+	    Own Id: OTP-18178 Aux Id: ERIERL-833, ERIERL-834,
+	    ERIERL-835 </p>
+        </item>
+        <item>
+          <p>
+	    The new function <c>ssh:daemon_replace_options/2</c>
+	    makes it possible to change the <c>Options</c> in a
+	    running SSH server.</p>
+          <p>
+	    Established connections are not affected, only those
+	    created after the call to this new function.</p>
+          <p>
+	    Own Id: OTP-18196</p>
+        </item>
+        <item>
+          <p>
+	    Add a timeout as option <c>max_initial_idle_time</c>. It
+	    closes a connection that does not allocate a channel
+	    within the timeout time.</p>
+          <p>
+	    For more information about timeouts, see the <seeguide
+	    marker="hardening#timeouts">Timeouts section </seeguide>
+	    in the User's Guide <seeguide
+	    marker="hardening">Hardening</seeguide> chapter.</p>
+          <p>
+	    Own Id: OTP-18207 Aux Id: PR-6231 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.14.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Binaries can be limited in logs with the parameter
+	    <c>max_log_item_len</c>. The default value is 500 bytes.</p>
+          <p>
+	    Own Id: OTP-18094</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.14</title>
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    The representation of Edward curves (ed25519 and ed448)
+	    inside ssh had a temporary representation (ed_pri and
+	    ed_pub).</p>
+          <p>
+	    That is now changed to the public_key form. See the
+	    manual for more information.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-17920</p>
+        </item>
+        <item>
+          <p>
+	    Former internal function
+	    <c>ssh_file:extract_public_key/1</c> documented publicly.</p>
+          <p>
+	    Internally it was previously in ssh_transport.</p>
+          <p>
+	    Own Id: OTP-18079 Aux Id: GH-5767 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.2.5</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, Curve25519 and Curve448 KEX methods
+	    become most preferred (related to RFC8731).</p>
+          <p>
+	    Own Id: OTP-18964 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.2.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, connection handler does not execute
+	    socket operations until it becomes socket owner.
+	    Previously errors could occur if connection handler tried
+	    to work with socket whose owner exited.</p>
+          <p>
+	    Own Id: OTP-18869 Aux Id: PR-7849,GH-7571 </p>
+        </item>
+        <item>
+          <p>
+	    With this change (being response to CVE-2023-48795), ssh
+	    can negotiate "strict KEX" OpenSSH extension with peers
+	    supporting it; also 'chacha20-poly1305@openssh.com'
+	    algorithm becomes a less preferred cipher.</p>
+          <p>
+	    If strict KEX availability cannot be ensured on both
+	    connection sides, affected encryption modes(CHACHA and
+	    CBC) can be disabled with standard ssh configuration.
+	    This will provide protection against vulnerability, but
+	    at a cost of affecting interoperability. See <seeguide
+	    marker="configure_algos">Configuring algorithms in
+	    SSH</seeguide>.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-18897</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.2.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, error logging related crashes in
+	    ssh_connection_handler module are fixed.</p>
+          <p>
+	    Own Id: OTP-18620 Aux Id: OTP-18386,PR-6611 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.2.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    With this change, ssh application does not crash when
+	    formatting some of info reports for unsuccessful
+	    connections.</p>
+          <p>
+	    Own Id: OTP-18386 Aux Id: PR-6611 </p>
+        </item>
+        <item>
+          <p>
+	    With this change, ssh does not log extensively long
+	    messages.</p>
+          <p>
+	    Own Id: OTP-18417 Aux Id: DAFH-1349,ERIERL-888,IA18357 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.2.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Binaries can be limited in logs with the parameter
+	    <c>max_log_item_len</c>. The default value is 500 bytes.</p>
+          <p>
+	    Own Id: OTP-18094</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Fix makefile dependency bugs.</p>
+          <p>
+	    Own Id: OTP-17847 Aux Id: PR-5574 GH-5548 </p>
+        </item>
+        <item>
+          <p>
+	    Fixed faulty OpenSSH decoding of Ed25519/Ed448 keys in
+	    the OpenSSH format <c>openssh_key_v1</c>.</p>
+          <p>
+	    Own Id: OTP-17868 Aux Id: PR-5520 </p>
+        </item>
+        <item>
+          <p>
+	    Correction of ssh_file typing, specially for the
+	    experimental openssh-key-v1 encoding.</p>
+          <p>
+	    Own Id: OTP-17912 Aux Id: GH-5680 </p>
+        </item>
+        <item>
+          <p>
+	    Improper tag for private ED keys when encoding with
+	    ssh:encode/2.</p>
+          <p>
+	    The tuple had <c>ed_priv</c> as first element, but should
+	    have had <c>ed_pri</c>. This is now corrected.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-17928 Aux Id: PR-5679 </p>
+        </item>
+      </list>
+    </section>
+
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    Add support for Ed25519/Ed448 SSH host keys in the RFC
+	    4716 format ("<c>-----BEGIN EC PRIVATE KEY-----</c>")
+	    generated by for example openssl or via Erlang functions
+	    (i.e. <c>public_key:generate_key({namedCurve,
+	    ed25519})</c>).</p>
+          <p>
+	    Ed25519 SSH host keys generated by <c>ssh-keygen</c> was,
+	    and are still, supported.</p>
+          <p>
+	    Own Id: OTP-17857 Aux Id: PR-5532 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    The ssh sever <c>parallel_login</c> option was missing in
+	    OTP-24</p>
+          <p>
+	    Own Id: OTP-17850 Aux Id: ERIERL-764 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.13</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    The value of the <c>connect_timeout</c> option is now
+	    used as default value for the negotiation timeout.</p>
+          <p>
+	    Own Id: OTP-17707 Aux Id: ERIERL-706 </p>
+        </item>
+      </list>
+    </section>
+
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    Add better error handling in connect/2,3,4. Detect
+	    incorrect arguments and return an informative error tuple
+	    instead of throwing a function_clause or similar.</p>
+          <p>
+	    Own Id: OTP-17515 Aux Id: ERIERL-648 </p>
+        </item>
+        <item>
+          <p>
+	    Make ssh algorithm selection better handle dynamic
+	    changes changes in crypto fips mode.</p>
+          <p>
+	    Own Id: OTP-17795</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.12.5</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Fixed a race condition in the acceptor loop: if a client
+	    disconnected immediately after the tcp connect, the
+	    server could cease handling connection on that
+	    address:port.</p>
+          <p>
+	    Own Id: OTP-17764 Aux Id: ERIERL-726 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.12.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Fixed that a slow start (&gt;30s) of a client subsystem
+	    could cause a log entry with the password.</p>
+          <p>
+	    Own Id: OTP-17390 Aux Id: ERIERL-648 </p>
+        </item>
+        <item>
+          <p>
+	    Fixed an error when running as an sftp server and a
+	    client requests a directory contents listing. </p>
+          <p>
+	    The fix is to handle the error code <c>{error,
+	    eacces}</c> as <c>{error, enoent}</c> in the
+	    <c>ssh_sftpd:get_attrs/5</c> internal function; that is,
+	    just skip it.</p>
+          <p>
+	    Own Id: OTP-17586 Aux Id: GH-5014 </p>
+        </item>
+      </list>
+    </section>
+
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    The "Key exchange failed" Info Report is now more
+	    informative.</p>
+          <p>
+	    Own Id: OTP-17450 Aux Id: ERIERL-655 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.12.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Filter out sensitive data (passwords etc) from progress
+	    reports and supervisor reports.</p>
+          <p>
+	    Own Id: OTP-17468 Aux Id: ERIERL-656 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.12.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Avoid an extra blank line in the ssh known_hosts file</p>
+          <p>
+	    Own Id: OTP-17427</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.12.1</title>
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    Add missing <c>known_hosts</c> and <c>authorized_keys</c>
+	    file types to <c>ssh_file:decode/2</c> and
+	    <c>ssh_file:encode/2</c>.</p>
+          <p>
+	    Own Id: OTP-17397</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.12</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+	    <p>Missing runtime dependencies has been added to this
+	    application.</p>
+          <p>
+	    Own Id: OTP-17243 Aux Id: PR-4557 </p>
+        </item>
+        <item>
+          <p>
+	    The send window handling is changed to not initialize a
+	    too large window on some occasions.</p>
+          <p>
+	    Own Id: OTP-17353</p>
+        </item>
+      </list>
+    </section>
+
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+          <p>
+	    Removed usage of <c>erlang:is_port/1</c> from the SSH
+	    implementation.</p>
+          <p>
+	    Own Id: OTP-16750</p>
+        </item>
+        <item>
+          <p>
+	    Internal connection setup refactoring.</p>
+          <p>
+	    Own Id: OTP-17051</p>
+        </item>
+        <item>
+          <p>
+	    Refactor SSH fsm into a (hopefully) more comprehensible
+	    set of gen_statem callback-files.</p>
+          <p>
+	    Own Id: OTP-17140</p>
+        </item>
+        <item>
+          <p>
+	    The RSA SHA1 sign/verify variants are disabled by
+	    default. That is, ssh-rsa is disabled by default as well
+	    as the SHA1 sign/verify with RSA keys from id_rsa and
+	    ssh_host_rsa_key. All SHA2 sign/verify are enabled by
+	    default.</p>
+          <p>
+	    The reason is that SHA1 is now considered easy to break.</p>
+          <p>
+	    To enable RSA with SHA1, for example for a very old and
+	    unsafe peer, see <seeguide
+	    marker="configure_algos#example-9">Example 9</seeguide>
+	    in the User's Guide chapter <seeguide
+	    marker="configure_algos">Configuring algorithms in
+	    SSH</seeguide>.</p>
+          <p>
+	    *** POTENTIAL INCOMPATIBILITY ***</p>
+          <p>
+	    Own Id: OTP-17259 Aux Id: OTP-16511, ERIERL-619 </p>
+        </item>
+        <item>
+          <p>
+	    Adapt ssh supervisors to the new 'significant' and
+	    'auto_shutdown' flags in supervisor.</p>
+          <p>
+	    Own Id: OTP-17322 Aux Id: PR-4638, EEP-56, OTP-17334 </p>
+        </item>
+        <item>
+          <p>
+	    The functions public_key:ssh_encode/2,
+	    public_key:ssh_decode/2,
+	    public_key:ssh_hostkey_fingerprint/1 and
+	    public_key:ssh_hostkey_fingerprint/2 are deprecated.</p>
+          <p>
+	    Replacement functions are available in SSH, see the
+	    <seeguide
+	    marker="system/general_info:deprecations#otp-24">Deprecations</seeguide>
+	    chapter in the Erlang/OTP documentation.</p>
+          <p>
+	    Own Id: OTP-17352</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
 <section><title>Ssh 4.11.1.6</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
@@ -283,6 +1461,22 @@
 
 </section>
 
+<section><title>Ssh 4.10.4.1</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Filter out sensitive data (passwords etc) from progress
+	    reports and supervisor reports.</p>
+          <p>
+	    Own Id: OTP-17468 Aux Id: ERIERL-656 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
 <section><title>Ssh 4.10.4</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
@@ -631,6 +1825,39 @@
 
 </section>
 
+<section><title>Ssh 4.9.1.4</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    The value of the <c>connect_timeout</c> option is now
+	    used as default value for the negotiation timeout.</p>
+          <p>
+	    Own Id: OTP-17707 Aux Id: ERIERL-706 </p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Ssh 4.9.1.3</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    The idle_time timer was not cancelled when a channel was
+	    opened within the timeout time on an empty connection
+	    that have had channels previously.</p>
+          <p>
+	    Own Id: OTP-17279</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
 <section><title>Ssh 4.9.1.2</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
@@ -782,7 +2009,7 @@
 	    input/output, the I/O was erroneously handled by the
 	    *server's* group leader, so the I/O turned up in the the
 	    server's Erlang shell (if any). The user at the client
-	    side did therefor not see that I/O.</p>
+	    side did therefore not see that I/O.</p>
           <p>
 	    This is corrected now, so the client - for example the
 	    ssh OS shell command - handles the I/O. The user could
@@ -968,6 +2195,23 @@
 
 </section>
 
+<section><title>Ssh 4.7.6.6</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    The idle_time timer was not cancelled when a channel was
+	    opened within the timeout time on an empty connection
+	    that have had channels previously.</p>
+          <p>
+	    Own Id: OTP-17279</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
 <section><title>Ssh 4.7.6.5</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
@@ -1310,7 +2554,7 @@
         </item>
         <item>
           <p>
-	    The type specifications in SSH are completly reworked and
+	    The type specifications in SSH are completely reworked and
 	    the following types are renamed:</p>
           <p>
 	    <c>ssh:ssh_connection_ref()</c> is changed to
@@ -1575,7 +2819,7 @@
         </item>
         <item>
           <p>
-	    Fix rare spurios shutdowns of ssh servers when receiveing
+	    Fix rare spurious shutdowns of ssh servers when receiving
 	    <c>{'EXIT',_,normal}</c> messages.</p>
           <p>
 	    Own Id: OTP-15018</p>
@@ -2026,7 +3270,7 @@
       <list>
         <item>
           <p>
-	    Fix rare spurios shutdowns of ssh servers when receiveing
+	    Fix rare spurious shutdowns of ssh servers when receiving
 	    <c>{'EXIT',_,normal}</c> messages.</p>
           <p>
 	    Own Id: OTP-15018</p>
@@ -2258,7 +3502,7 @@
         <item>
           <p>
 	    If a client illegaly sends an info-line and then
-	    immediatly closes the TCP-connection, a badmatch
+	    immediately closes the TCP-connection, a badmatch
 	    exception was raised.</p>
           <p>
 	    Own Id: OTP-13966</p>
@@ -2471,7 +3715,7 @@
       <list>
         <item>
           <p>
-	    Fix rare spurios shutdowns of ssh servers when receiveing
+	    Fix rare spurious shutdowns of ssh servers when receiving
 	    <c>{'EXIT',_,normal}</c> messages.</p>
           <p>
 	    Own Id: OTP-15018</p>
@@ -3093,7 +4337,7 @@
           <p>
 	    The possible values are: <c>{id_string,string()}</c> and
 	    <c>{id_string,random}</c>. The latter will make ssh
-	    generate a random nonsence id-string for each new
+	    generate a random nonsense id-string for each new
 	    connection.</p>
           <p>
 	    Own Id: OTP-12659</p>
@@ -3219,7 +4463,7 @@
           <p>
 	    The possible values are: <c>{id_string,string()}</c> and
 	    <c>{id_string,random}</c>. The latter will make ssh
-	    generate a random nonsence id-string for each new
+	    generate a random nonsense id-string for each new
 	    connection.</p>
           <p>
 	    Own Id: OTP-12659</p>
@@ -3882,7 +5126,7 @@
       <list>
         <item>
           <p>
-	    ssh:daemon will get feeded with an argument even if it is
+	    ssh:daemon will get fed with an argument even if it is
 	    not a valid expression.</p>
           <p>
 	    Own Id: OTP-10975</p>
@@ -4230,7 +5474,7 @@
       <list>
         <item>
           <p>
-	    All keys in authorized_keys are considerd, wrongly only
+	    All keys in authorized_keys are considered, wrongly only
 	    the first one was before.</p>
           <p>
 	    Own Id: OTP-7235</p>
@@ -4604,7 +5848,7 @@
       <list>
         <item>
           <p>
-            Now clear all processes when a connnection is terminated.</p>
+            Now clear all processes when a connection is terminated.</p>
           <p>
             Own Id: OTP-8121 Aux Id:</p>
         </item>
@@ -4702,13 +5946,13 @@
       <list>
         <item>
           <p>
-            ssh_sftp:start_channel/3 did not handle timout correctly.</p>
+            ssh_sftp:start_channel/3 did not handle timeout correctly.</p>
           <p>
             Own Id: OTP-8159 Aux Id: seq11386</p>
         </item>
         <item>
           <p>
-            If a progress message was not recieved after invoking ssh:connect/3
+            If a progress message was not received after invoking ssh:connect/3
             the call could hang for ever. A timeout option has also been added.</p>
           <p>
             Own Id: OTP-8160 Aux Id: seq11386</p>
Index: otp-OTP-23.3.4.19/lib/ssh/doc/src/SSH_app.xml
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/doc/src/SSH_app.xml
+++ otp-OTP-23.3.4.19/lib/ssh/doc/src/SSH_app.xml
@@ -4,7 +4,7 @@
 <appref>
   <header>
     <copyright>
-      <year>2012</year><year>2020</year>
+      <year>2012</year><year>2024</year>
       <holder>Ericsson AB. All Rights Reserved.</holder>
     </copyright>
     <legalnotice>
@@ -172,16 +172,16 @@
       <tag>Key exchange algorithms</tag>
       <item>
 	<list type="bulleted">
-	  <item>ecdh-sha2-nistp384</item>
+	  <item>curve25519-sha256</item>
+	  <item>curve25519-sha256@libssh.org</item>
+	  <item>curve448-sha512</item>
 	  <item>ecdh-sha2-nistp521</item>
+	  <item>ecdh-sha2-nistp384</item>
 	  <item>ecdh-sha2-nistp256</item>
 	  <item>diffie-hellman-group-exchange-sha256</item>
 	  <item>diffie-hellman-group16-sha512</item>
 	  <item>diffie-hellman-group18-sha512</item>
 	  <item>diffie-hellman-group14-sha256</item>
-	  <item>curve25519-sha256</item>
-	  <item>curve25519-sha256@libssh.org</item>
-	  <item>curve448-sha512</item>
 	</list>
 	<p>The following unsecure <c>SHA1</c> algorithms are now disabled by default:</p>
 	<list>
@@ -199,18 +199,18 @@
       <tag>Public key algorithms</tag>
       <item>
 	<list type="bulleted">
-	  <item>ecdsa-sha2-nistp384</item>
-	  <item>ecdsa-sha2-nistp521</item>
-	  <item>ecdsa-sha2-nistp256</item>
-	  <item>ssh-ed25519</item>
+          <item>ssh-ed25519</item>
 	  <item>ssh-ed448</item>
+          <item>ecdsa-sha2-nistp521</item>
+          <item>ecdsa-sha2-nistp384</item>
+	  <item>ecdsa-sha2-nistp256</item>
+          <item>rsa-sha2-512</item>
 	  <item>rsa-sha2-256</item>
-	  <item>rsa-sha2-512</item>
-	  <item>ssh-rsa <i>(SHA1 sign/verify are supported but disabled by default from OTP-24)</i></item>
 	</list>
-	<p>The following unsecure <c>SHA1</c> algorithm is supported but disabled by default:</p>
+	<p>The following unsecure <c>SHA1</c> algorithms are supported but disabled by default:</p>
 	<list>
 	  <item>(ssh-dss)</item>
+	  <item>(ssh-rsa)</item>
 	</list>
 	<p>See 
 	Disabled public key algorithms can be enabled with the
@@ -227,11 +227,11 @@
       <tag>MAC algorithms</tag>
       <item>
 	<list type="bulleted">
-	  <item>hmac-sha2-256-etm@openssh.com</item>
 	  <item>hmac-sha2-512-etm@openssh.com</item>
-	  <item>hmac-sha1-etm@openssh.com</item>
-	  <item>hmac-sha2-256</item>
+	  <item>hmac-sha2-256-etm@openssh.com</item>
 	  <item>hmac-sha2-512</item>
+	  <item>hmac-sha2-256</item>
+          <item>hmac-sha1-etm@openssh.com</item>
 	  <item>hmac-sha1</item>
 	</list>
 	<p>The following unsecure <c>SHA1</c> algorithm is disabled by default:</p>
@@ -282,7 +282,7 @@
   </section>
   <section>
     <title>Unicode support</title>
-    <p>Unicode filenames are supported if the emulator and the underlaying OS support it. See section DESCRIPTION in the
+    <p>Unicode filenames are supported if the emulator and the underlying OS support it. See section DESCRIPTION in the
       <seeerl marker="kernel:file">file</seeerl> manual page in Kernel for information about this subject.
     </p>
     <p>The shell and the cli both support unicode.
@@ -318,6 +318,7 @@
 	<item>6.6.  Public Key Algorithms
 	<list type="bulleted">
 	  <item>ssh-dss</item>
+	  <item>ssh-rsa</item>
 	</list>
 	</item>
       </list>
@@ -450,7 +451,7 @@
       </item>
 
       <item>
-	<url href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-curves">Secure Shell (SSH) Key Exchange Method using Curve25519 and Curve448 (work in progress)</url>
+	<url href="https://tools.ietf.org/html/rfc8731">Secure Shell (SSH) Key Exchange Method Using Curve25519 and Curve448</url>
 	<p/>
       </item>
 
Index: otp-OTP-23.3.4.19/lib/ssh/doc/src/ssh_sftpd.xml
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/doc/src/ssh_sftpd.xml
+++ otp-OTP-23.3.4.19/lib/ssh/doc/src/ssh_sftpd.xml
@@ -4,7 +4,7 @@
 <erlref>
   <header>
     <copyright>
-      <year>2005</year><year>2020</year>
+      <year>2005</year><year>2025</year>
       <holder>Ericsson AB. All Rights Reserved.</holder>
     </copyright>
     <legalnotice>
@@ -65,6 +65,18 @@
 	    If supplied, the number of filenames returned to the SFTP client per <c>READDIR</c>
 	    request is limited to at most the given value.</p>
 	  </item>
+	  <tag><c>max_handles</c></tag>
+	  <item>
+            <p>The default value is <c>1000</c>. Positive integer value represents the maximum number of file handles allowed for a connection.</p>            
+	  </item>
+          <tag><c>max_path</c></tag>
+	  <item>
+	    <p>The default value is <c>4096</c>. Positive integer
+	    value represents the maximum path length which cannot be
+	    exceeded in data provided by the SFTP client. (Note:
+	    limitations might be also enforced by underlying operating
+	    system)</p>
+	  </item>
 	  <tag><c>root</c></tag>
 	  <item>
 	    <p>Sets the SFTP root directory. Then the user cannot see any files
Index: otp-OTP-23.3.4.19/lib/ssh/src/.gitignore
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/src/.gitignore
@@ -0,0 +1 @@
+deps
Index: otp-OTP-23.3.4.19/lib/ssh/src/Makefile
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/Makefile
+++ otp-OTP-23.3.4.19/lib/ssh/src/Makefile
@@ -1,7 +1,7 @@
 #
 # %CopyrightBegin%
 #
-# Copyright Ericsson AB 2004-2020. All Rights Reserved.
+# Copyright Ericsson AB 2004-2024. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -66,11 +66,13 @@ MODULES= \
 	ssh_cli \
 	ssh_connection \
 	ssh_connection_handler \
-	ssh_connection_sup \
-	ssh_controller \
 	ssh_file \
+	ssh_fsm_kexinit \
+	ssh_fsm_userauth_client \
+	ssh_fsm_userauth_server \
 	ssh_info \
 	ssh_io \
+	ssh_lib \
 	ssh_message \
 	ssh_no_io \
 	ssh_options \
@@ -78,17 +80,14 @@ MODULES= \
 	ssh_sftpd \
 	ssh_sftpd_file\
 	ssh_shell \
-	ssh_subsystem_sup \
-	ssh_sup \
+	ssh_connection_sup \
 	ssh_system_sup \
 	ssh_tcpip_forward_srv \
 	ssh_tcpip_forward_client \
 	ssh_tcpip_forward_acceptor_sup \
 	ssh_tcpip_forward_acceptor \
 	ssh_transport \
-	ssh_xfer \
-	sshc_sup \
-	sshd_sup
+	ssh_xfer
 
 HRL_FILES =
 
@@ -113,7 +112,25 @@ APP_TARGET= $(EBIN)/$(APP_FILE)
 APPUP_SRC= $(APPUP_FILE).src
 APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
 
-INTERNAL_HRL_FILES = ssh_agent.hrl ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_xfer.hrl
+INTERNAL_HRL_FILES = \
+	ssh.hrl \
+	ssh_agent.hrl \
+	ssh_auth.hrl \
+	ssh_connect.hrl \
+	ssh_fsm.hrl \
+	ssh_transport.hrl \
+	ssh_xfer.hrl
+
+DEPDIR=$(ERL_TOP)/lib/ssh/src/deps
+DEP_FILE=$(DEPDIR)/ssh.d
+$(shell mkdir -p $(dir $(DEP_FILE)) >/dev/null)
+
+ifeq ($(TARGET), win32)
+  # Native path without C: ignore driveletter case
+  ERL_TOP_NATIVE = $(shell w32_path.sh -m $(ERL_TOP) | sed "s@[a-zA-Z]:@:@")
+else
+  ERL_TOP_NATIVE = $(ERL_TOP)
+endif
 
 # ----------------------------------------------------
 # FLAGS
@@ -132,11 +149,22 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/ke
 $(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES_2)
 $(BEHAVIOUR_TARGET_FILES_2): $(BEHAVIOUR_TARGET_FILES_1)
 
-debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+$(DEP_FILE): $(ERL_FILES)
+	@echo SED $(TARGET) $(ERL_TOP_NATIVE)
+	$(gen_verbose)erlc -M $(ERL_FILES) \
+	| perl -pe "s@ [a-zA-Z]?$(ERL_TOP_NATIVE)/(?:bootstrap/)?lib/([^/]+)@ ../../\1@g" 2> /dev/null \
+	| sed "s/\.$(EMULATOR)/\.$$\(EMULATOR\)/" \
+	| sed 's@^ssh_@$$(EBIN)/ssh_@' \
+	| sed 's@^sshc_@$$(EBIN)/sshc_@' \
+	| sed 's@^sshd_@$$(EBIN)/sshd_@' \
+	> $(DEP_FILE)
+
+$(TYPES): $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(DEP_FILE)
 
 clean:
 	rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES)
 	rm -f errs core *~
+	rm -rf $(DEPDIR)
 
 $(APP_TARGET):	$(APP_SRC) ../vsn.mk
 	$(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
@@ -163,81 +191,7 @@ release_spec: opt
 
 release_docs_spec:
 
-
-deps:
-	erlc -M $(ERL_FILES) \
-	| sed 's@$(ERL_TOP)/lib@../..@g' \
-	| sed 's/\.$(EMULATOR)/\.$$\(EMULATOR\)/' \
-	| sed 's@^ssh_@$$(EBIN)/ssh_@'
-
-ssh.$(EMULATOR): ssh.erl ssh.hrl ssh_connect.hrl \
-  ../../public_key/include/public_key.hrl \
-  ../../public_key/include/OTP-PUB-KEY.hrl \
-  ../../public_key/include/PKCS-FRAME.hrl \
-  ../../kernel/include/file.hrl
-$(EBIN)/ssh_sup.$(EMULATOR): ssh_sup.erl
-sshc_sup.$(EMULATOR): sshc_sup.erl
-sshd_sup.$(EMULATOR): sshd_sup.erl ssh.hrl
-$(EBIN)/ssh_connection_sup.$(EMULATOR): ssh_connection_sup.erl
-$(EBIN)/ssh_connection.$(EMULATOR): ssh_connection.erl ssh.hrl ssh_connect.hrl \
-  ssh_transport.hrl
-$(EBIN)/ssh_connection_handler.$(EMULATOR): ssh_connection_handler.erl ssh.hrl \
-  ssh_transport.hrl ssh_auth.hrl ssh_connect.hrl
-$(EBIN)/ssh_shell.$(EMULATOR): ssh_shell.erl ssh_connect.hrl
-$(EBIN)/ssh_system_sup.$(EMULATOR): ssh_system_sup.erl ssh.hrl
-$(EBIN)/ssh_subsystem_sup.$(EMULATOR): ssh_subsystem_sup.erl
-$(EBIN)/ssh_channel_sup.$(EMULATOR): ssh_channel_sup.erl ssh.hrl
-$(EBIN)/ssh_acceptor_sup.$(EMULATOR): ssh_acceptor_sup.erl ssh.hrl
-$(EBIN)/ssh_acceptor.$(EMULATOR): ssh_acceptor.erl ssh.hrl
-$(EBIN)/ssh_agent.$(EMULATOR): ssh_agent.erl ssh.hrl ssh_agent.hrl
-$(EBIN)/ssh_app.$(EMULATOR): ssh_app.erl
-$(EBIN)/ssh_auth.$(EMULATOR): ssh_auth.erl \
-  ../../public_key/include/public_key.hrl \
-  ../../public_key/include/OTP-PUB-KEY.hrl \
-  ../../public_key/include/PKCS-FRAME.hrl \
-  ssh.hrl ssh_auth.hrl ssh_transport.hrl
-$(EBIN)/ssh_bits.$(EMULATOR): ssh_bits.erl ssh.hrl
-$(EBIN)/ssh_cli.$(EMULATOR): ssh_cli.erl ssh.hrl ssh_connect.hrl
-$(EBIN)/ssh_file.$(EMULATOR): ssh_file.erl \
-  ../../public_key/include/public_key.hrl \
-  ../../public_key/include/OTP-PUB-KEY.hrl \
-  ../../public_key/include/PKCS-FRAME.hrl \
-  ../../kernel/include/file.hrl ssh.hrl
-$(EBIN)/ssh_io.$(EMULATOR): ssh_io.erl ssh.hrl
-$(EBIN)/ssh_info.$(EMULATOR): ssh_info.erl
-$(EBIN)/ssh_message.$(EMULATOR): ssh_message.erl \
-  ../../public_key/include/public_key.hrl \
-  ../../public_key/include/OTP-PUB-KEY.hrl \
-  ../../public_key/include/PKCS-FRAME.hrl \
-  ssh.hrl ssh_connect.hrl ssh_auth.hrl ssh_transport.hrl
-$(EBIN)/ssh_no_io.$(EMULATOR): ssh_no_io.erl ssh_transport.hrl
-$(EBIN)/ssh_sftp.$(EMULATOR): ssh_sftp.erl \
-  ../../kernel/include/file.hrl ssh.hrl \
-  ssh_xfer.hrl
-$(EBIN)/ssh_sftpd.$(EMULATOR): ssh_sftpd.erl \
-  ../../kernel/include/file.hrl ssh.hrl \
-  ssh_xfer.hrl
-$(EBIN)/ssh_sftpd_file.$(EMULATOR): ssh_sftpd_file.erl
-$(EBIN)/ssh_transport.$(EMULATOR): ssh_transport.erl \
-  ../../public_key/include/public_key.hrl \
-  ../../public_key/include/OTP-PUB-KEY.hrl \
-  ../../public_key/include/PKCS-FRAME.hrl \
-  ../../kernel/include/inet.hrl \
-  ssh_transport.hrl ssh.hrl
-$(EBIN)/ssh_xfer.$(EMULATOR): ssh_xfer.erl ssh.hrl ssh_xfer.hrl
-$(EBIN)/ssh_sftpd_file_api.$(EMULATOR): ssh_sftpd_file_api.erl
-$(EBIN)/ssh_client_channel.$(EMULATOR): ssh_client_channel.erl ssh_connect.hrl
-$(EBIN)/ssh_channel.$(EMULATOR): ssh_channel.erl ssh_connect.hrl
-$(EBIN)/ssh_daemon_channel.$(EMULATOR): ssh_daemon_channel.erl
-$(EBIN)/ssh_server_channel.$(EMULATOR): ssh_server_channel.erl
-$(EBIN)/ssh_client_key_api.$(EMULATOR): ssh_client_key_api.erl \
-  ../../public_key/include/public_key.hrl \
-  ../../public_key/include/OTP-PUB-KEY.hrl \
-  ../../public_key/include/PKCS-FRAME.hrl \
-  ssh.hrl
-$(EBIN)/ssh_server_key_api.$(EMULATOR): ssh_server_key_api.erl \
-  ../../public_key/include/public_key.hrl \
-  ../../public_key/include/OTP-PUB-KEY.hrl \
-  ../../public_key/include/PKCS-FRAME.hrl \
-  ssh.hrl
-
+# ----------------------------------------------------
+# Dependencies
+# ----------------------------------------------------
+-include $(DEP_FILE)
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_acceptor.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_acceptor.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_acceptor.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -25,82 +25,29 @@
 -include("ssh.hrl").
 
 %% Internal application API
--export([start_link/4,
+-export([start_link/3,
 	 number_of_connections/1,
-	 listen/2,
-	 handle_established_connection/4]).
+	 listen/2]).
 
 %% spawn export  
--export([acceptor_init/5, acceptor_loop/6]).
+-export([acceptor_init/4, acceptor_loop/6]).
 
 -behaviour(ssh_dbg).
--export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2, ssh_dbg_format/3]).
 
 -define(SLEEP_TIME, 200).
 
 %%====================================================================
 %% Internal application API
 %%====================================================================
-start_link(Port, Address, Options, AcceptTimeout) ->
-    Args = [self(), Port, Address, Options, AcceptTimeout],
-    proc_lib:start_link(?MODULE, acceptor_init, Args).
-
-%%%----------------------------------------------------------------
-number_of_connections(SysSup) ->
-    length([S || S <- supervisor:which_children(SysSup),
-                 has_worker(SysSup,S)]).
-
-
-has_worker(SysSup, {R,SubSysSup,supervisor,[ssh_subsystem_sup]}) when is_reference(R),
-                                                                      is_pid(SubSysSup) ->
-    try
-        {{server, ssh_connection_sup, _, _}, Pid, supervisor, [ssh_connection_sup]} =
-            lists:keyfind([ssh_connection_sup], 4, supervisor:which_children(SubSysSup)),
-        {Pid, supervisor:which_children(Pid)}
-    of
-        {ConnSup,[]} ->
-            %% Strange. Since the connection supervisor exists, there should have been
-            %% a connection here.
-            %% It might be that the connection_handler worker has "just died", maybe
-            %% due to a exit(_,kill). It might also be so that the worker is starting.
-            %% Spawn a killer that redo the test and kills it if the problem persists.
-            %% TODO: Fix this better in the supervisor tree....
-            spawn(fun() ->
-                          timer:sleep(10),
-                          try supervisor:which_children(ConnSup)
-                          of
-                              [] ->
-                                  %% we are on the server-side:
-                                  ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
-                              [_] ->
-                                  %% is ok now
-                                  ok;
-                          _ ->
-                                  %% What??
-                                  error
-                          catch _:_ ->
-                                  %% What??
-                                  error
-                          end
-                  end),
-            false;
-        {_ConnSup,[_]}->
-            true;
-         _ ->
-            %% What??
-            false
-    catch _:_ ->
-            %% What??
-            false
-    end;
-
-has_worker(_,_) ->
-    false.
+%% Supposed to be called in a child-spec of the ssh_acceptor_sup
+start_link(SystemSup, Address, Options) ->
+    proc_lib:start_link(?MODULE, acceptor_init, [self(),SystemSup,Address,Options]).
 
 %%%----------------------------------------------------------------
 listen(Port, Options) ->
     {_, Callback, _} = ?GET_OPT(transport, Options),
-    SockOpts = [{active, false}, {reuseaddr,true} | ?GET_OPT(socket_options, Options)],
+    SockOpts = ?GET_OPT(socket_options, Options) ++ [{active, false}, {reuseaddr,true}],
     case Callback:listen(Port, SockOpts) of
 	{error, nxdomain} ->
 	    Callback:listen(Port, lists:delete(inet6, SockOpts));
@@ -112,40 +59,53 @@ listen(Port, Options) ->
 	    Other
     end.
 
-%%%----------------------------------------------------------------
-handle_established_connection(Address, Port, Options, Socket) ->
+accept(ListenSocket, AcceptTimeout, Options) ->
     {_, Callback, _} = ?GET_OPT(transport, Options),
-    handle_connection(Callback, Address, Port, Options, Socket).
+    Callback:accept(ListenSocket, AcceptTimeout).
 
+close(Socket, Options) ->
+    {_, Callback, _} = ?GET_OPT(transport, Options),
+    Callback:close(Socket).
+    
 %%--------------------------------------------------------------------
 %%% Internal functions
 %%--------------------------------------------------------------------
-acceptor_init(Parent, Port, Address, Opts, AcceptTimeout) ->
-    try
-        ?GET_INTERNAL_OPT(lsocket, Opts)
-    of
+acceptor_init(Parent, SystemSup,
+              #address{address=Address, port=Port, profile=_Profile},
+              Opts) ->
+    AcceptTimeout = ?GET_INTERNAL_OPT(timeout, Opts, ?DEFAULT_TIMEOUT),
+    case ?GET_INTERNAL_OPT(lsocket, Opts, undefined) of
         {LSock, SockOwner} ->
+            %% A listening socket (or fd option) was provided in the ssh:daemon call
             case inet:sockname(LSock) of
-                {ok,{_,Port}} -> % A usable, open LSock
+                {ok,{_,Port}} ->
+                    %% A usable, open LSock
                     proc_lib:init_ack(Parent, {ok, self()}),
                     request_ownership(LSock, SockOwner),
-                    {_, Callback, _} =  ?GET_OPT(transport, Opts),
-                    acceptor_loop(Callback, Port, Address, Opts, LSock, AcceptTimeout);
-
-                {error,_} -> % Not open, a restart
-                    %% Allow gen_tcp:listen to fail 4 times if eaddrinuse:
-                    {ok,NewLSock} = try_listen(Port, Opts, 4),
+                    acceptor_loop(Port, Address, Opts, LSock, AcceptTimeout, SystemSup);
+                {error,_Error} ->
+                    %% Not open, a restart
+                    %% Allow gen_tcp:listen to fail 4 times if eaddrinuse (It is a bug fix):
+                    case try_listen(Port, Opts, 4) of
+                        {ok,NewLSock} ->
+                            proc_lib:init_ack(Parent, {ok, self()}),
+                            Opts1 = ?DELETE_INTERNAL_OPT(lsocket, Opts),
+                            acceptor_loop(Port, Address, Opts1, NewLSock, AcceptTimeout, SystemSup);
+                        {error,Error} ->
+                            proc_lib:init_fail(Parent, {error,Error}, {exit, normal})
+                    end
+            end;
+        undefined ->
+            %% No listening socket (nor fd option) was provided; open a listening socket:
+            case try_listen(Port, Opts, 4) of
+                {ok,LSock} ->
                     proc_lib:init_ack(Parent, {ok, self()}),
-                    Opts1 = ?DELETE_INTERNAL_OPT(lsocket, Opts),
-                    {_, Callback, _} =  ?GET_OPT(transport, Opts1),
-                    acceptor_loop(Callback, Port, Address, Opts1, NewLSock, AcceptTimeout)
+                    acceptor_loop(Port, Address, Opts, LSock, AcceptTimeout, SystemSup);
+                {error,Error} ->
+                    proc_lib:init_fail(Parent, {error,Error}, {exit, normal})
             end
-    catch
-        _:_ ->
-            {error,use_existing_socket_failed}
     end.
 
-
 try_listen(Port, Opts, NtriesLeft) ->
     try_listen(Port, Opts, 1, NtriesLeft).
 
@@ -158,7 +118,6 @@ try_listen(Port, Opts, N, Nmax) ->
             Other
     end.
 
-
 request_ownership(LSock, SockOwner) ->
     SockOwner ! {request_control,LSock,self()},
     receive
@@ -166,117 +125,217 @@ request_ownership(LSock, SockOwner) ->
     end.
     
 %%%----------------------------------------------------------------    
-acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
-    case (catch Callback:accept(ListenSocket, AcceptTimeout)) of
-	{ok, Socket} ->
-	    handle_connection(Callback, Address, Port, Opts, Socket),
-	    ?MODULE:acceptor_loop(Callback, Port, Address, Opts,
-				  ListenSocket, AcceptTimeout);
-	{error, Reason} ->
-	    handle_error(Reason),
-	    ?MODULE:acceptor_loop(Callback, Port, Address, Opts,
-				  ListenSocket, AcceptTimeout);
-	{'EXIT', Reason} ->
-	    handle_error(Reason),
-	    ?MODULE:acceptor_loop(Callback, Port, Address, Opts,
-				  ListenSocket, AcceptTimeout)
-    end.
+acceptor_loop(Port, Address, Opts, ListenSocket, AcceptTimeout, SystemSup) ->
+    try
+        case accept(ListenSocket, AcceptTimeout, Opts) of
+            {ok,Socket} ->
+                PeerName = inet:peername(Socket),
+                MaxSessions = ?GET_OPT(max_sessions, Opts),
+                NumSessions = number_of_connections(SystemSup),
+                ParallelLogin = ?GET_OPT(parallel_login, Opts),
+                case handle_connection(Address, Port, PeerName, Opts, Socket,
+                                       MaxSessions, NumSessions, ParallelLogin) of
+                    {error,Error} ->
+                        catch close(Socket, Opts),
+                        handle_error(Error, Address, Port, PeerName);
+                    _ ->
+                        ok
+                end;
+            {error,Error} ->
+                handle_error(Error, Address, Port, undefined)
+        end
+    catch
+        Class:Err:Stack ->
+            handle_error({error, {unhandled,Class,Err,Stack}}, Address, Port, undefined)
+    end,
+    ?MODULE:acceptor_loop(Port, Address, Opts, ListenSocket, AcceptTimeout, SystemSup).
 
 %%%----------------------------------------------------------------
-handle_connection(Callback, Address, Port, Options, Socket) ->
-    Profile =  ?GET_OPT(profile, Options),
-    SystemSup = ssh_system_sup:system_supervisor(Address, Port, Profile),
-
-    MaxSessions = ?GET_OPT(max_sessions, Options),
-    case number_of_connections(SystemSup) < MaxSessions of
-	true ->
-	    {ok, SubSysSup} = 
-                ssh_system_sup:start_subsystem(SystemSup, server, Address, Port, Profile, Options),
-	    ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
-	    NegTimeout = ?GET_OPT(negotiation_timeout, Options),
-	    ssh_connection_handler:start_connection(server, Socket,
-                                                    ?PUT_INTERNAL_OPT(
-                                                       {supervisors, [{system_sup, SystemSup},
-                                                                      {subsystem_sup, SubSysSup},
-                                                                      {connection_sup, ConnectionSup}]},
-                                                       Options), NegTimeout);
-	false ->
-	    Callback:close(Socket),
-	    IPstr = if is_tuple(Address) -> inet:ntoa(Address);
-		     true -> Address
-		  end,
-	    Str = try io_lib:format('~s:~p',[IPstr,Port])
-		  catch _:_ -> "port "++integer_to_list(Port)
-		  end,
-	    error_logger:info_report("Ssh login attempt to "++Str++" denied due to option "
-				     "max_sessions limits to "++ io_lib:write(MaxSessions) ++
-				     " sessions."
-				     ),
-	    {error,max_sessions}
-    end.
+handle_connection(_Address, _Port, _Peer, _Options, _Socket,
+                  MaxSessions, NumSessions, _ParallelLogin)
+  when NumSessions >= MaxSessions->
+    {error,{max_sessions,MaxSessions}};
+handle_connection(_Address, _Port, {error,Error}, _Options, _Socket,
+                  _MaxSessions, _NumSessions, _ParallelLogin) ->
+    {error,Error};
+handle_connection(Address, Port, _Peer, Options, Socket,
+                  _MaxSessions, _NumSessions, ParallelLogin)
+  when ParallelLogin == false ->
+    handle_connection(Address, Port, Options, Socket);
+handle_connection(Address, Port, _Peer, Options, Socket,
+                  _MaxSessions, _NumSessions, ParallelLogin)
+  when ParallelLogin == true ->
+    Ref = make_ref(),
+    Pid = spawn_link(
+            fun() ->
+                    process_flag(trap_exit, true),
+                    receive
+                        {start,Ref} ->
+                            handle_connection(Address, Port, Options, Socket)
+                    after 10000 ->
+                            {error, timeout2}
+                    end
+            end),
+    catch gen_tcp:controlling_process(Socket, Pid),
+    Pid ! {start,Ref},
+    ok.
+
+handle_connection(Address, Port, Options0, Socket) ->
+    Options = ?PUT_INTERNAL_OPT([{user_pid, self()}
+                                ], Options0),
+    ssh_system_sup:start_connection(server,
+                                   #address{address = Address,
+                                            port = Port,
+                                            profile = ?GET_OPT(profile,Options)
+                                           },
+                                   Socket,
+                                   Options).
 
 %%%----------------------------------------------------------------
-handle_error(timeout) ->
-    ok;
+handle_error(Reason, ToAddress, ToPort, {ok, {FromIP,FromPort}}) ->
+    handle_error(Reason, ToAddress, ToPort, FromIP, FromPort);
+
+handle_error(Reason, ToAddress, ToPort, _) ->
+    handle_error(Reason, ToAddress, ToPort, undefined, undefined).
 
-handle_error(enfile) ->
-    %% Out of sockets...
-    timer:sleep(?SLEEP_TIME);
-
-handle_error(emfile) ->
-    %% Too many open files -> Out of sockets...
-    timer:sleep(?SLEEP_TIME);
-
-handle_error(closed) ->
-    error_logger:info_report("The ssh accept socket was closed by " 
-			     "a third party. "
-			     "This will not have an impact on ssh "
-			     "that will open a new accept socket and " 
-			     "go on as nothing happened. It does however "
-			     "indicate that some other software is behaving "
-			     "badly."),
-    exit(normal);
-
-handle_error(Reason) ->
-    String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])),
-    error_logger:error_report(String),
-    exit({accept_failed, String}).    
+
+handle_error(Reason, ToAddress, ToPort, FromAddress, FromPort) ->
+    case Reason of
+        {max_sessions, MaxSessions} ->
+            MsgFun =
+                fun(debug) ->
+                        lists:concat(["Ssh login attempt to ",
+                                      ssh_lib:format_address_port(ToAddress,ToPort),
+                                      " from ",
+                                      ssh_lib:format_address_port(FromAddress,FromPort),
+                                      " denied due to option max_sessions limits to ",
+                                      MaxSessions, " sessions."]);
+                   (_) ->
+                        ["Ssh login attempt denied max_session limits"]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun));
+        Limit when Limit==enfile ; Limit==emfile ->
+            %% Out of sockets...
+            MsgFun =
+                fun(debug) ->
+                        [atom_to_list(Limit),": out of accept sockets on ",
+                         ssh_lib:format_address_port(ToAddress, ToPort),
+                         " - retrying"];
+                   (_) ->
+                        ["Out of accept sockets on - retrying"]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun)),
+            timer:sleep(?SLEEP_TIME);
+        closed ->
+            MsgFun =
+                fun(debug) ->
+                        ["The ssh accept socket on ", ssh_lib:format_address_port(ToAddress,ToPort),
+                         "was closed by a third party."];
+                   (_) ->
+                        ["The ssh accept socket on was closed by a third party"]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun));
+        timeout ->
+            ok;
+        Error when is_list(Error) ->
+            ok;
+        Error when FromAddress=/=undefined,
+                   FromPort=/=undefined ->
+            MsgFun =
+                fun(debug) ->
+                        ["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
+                         " for connect from ",ssh_lib:format_address_port(FromAddress,FromPort),
+                         io_lib:format(": ~p", [Error])];
+                   (_) ->
+                        [io_lib:format("Accept failed on for connection: ~p", [Error])]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun));
+        Error ->
+            MsgFun =
+                fun(debug) ->
+                        ["Accept failed on ",ssh_lib:format_address_port(ToAddress,ToPort),
+                         io_lib:format(": ~p", [Error])];
+                   (_) ->
+                        [io_lib:format("Accept failed on for connection: ~p", [Error])]
+                end,
+            error_logger:info_report(?SELECT_MSG(MsgFun))
+    end.
+
+%%%----------------------------------------------------------------
+number_of_connections(SysSupPid) ->
+    lists:foldl(fun({_Ref,_Pid,supervisor,[ssh_connection_sup]}, N) -> N+1;
+                   (_, N) -> N
+                end, 0, supervisor:which_children(SysSupPid)).
 
 %%%################################################################
 %%%#
 %%%# Tracing
 %%%#
 
-ssh_dbg_trace_points() -> [connections].
+ssh_dbg_trace_points() -> [connections, tcp].
 
+ssh_dbg_flags(tcp) -> [c];
 ssh_dbg_flags(connections) -> [c].
 
-ssh_dbg_on(connections) -> dbg:tp(?MODULE,  acceptor_init, 5, x),
-                           dbg:tpl(?MODULE, handle_connection, 5, x).
-
-ssh_dbg_off(connections) -> dbg:ctp(?MODULE, acceptor_init, 5),
-                            dbg:ctp(?MODULE, handle_connection, 5).
+ssh_dbg_on(tcp) -> dbg:tp(?MODULE, listen, 2, x),
+                   dbg:tpl(?MODULE, accept, 3, x),
+                   dbg:tpl(?MODULE, close, 2, x);
+                   
+ssh_dbg_on(connections) -> dbg:tp(?MODULE,  acceptor_init, 4, x),
+                           dbg:tpl(?MODULE, handle_connection, 4, x).
+
+ssh_dbg_off(tcp) -> dbg:ctpg(?MODULE, listen, 2),
+                    dbg:ctpl(?MODULE, accept, 3),
+                    dbg:ctpl(?MODULE, close, 2);
+
+ssh_dbg_off(connections) -> dbg:ctp(?MODULE, acceptor_init, 4),
+                            dbg:ctp(?MODULE, handle_connection, 4).
+
+ssh_dbg_format(tcp, {call, {?MODULE,listen, [Port,_Opts]}}, Stack) ->
+    {skip, [{port,Port}|Stack]};
+ssh_dbg_format(tcp, {return_from, {?MODULE,listen,2}, {ok,Sock}}, [{port,Port}|Stack]) ->
+    {["TCP listener started\n",
+      io_lib:format("Port: ~p~n"
+                    "ListeningSocket: ~p~n", [Port,Sock])
+     ],
+     Stack};
+ssh_dbg_format(tcp, {return_from, {?MODULE,listen,2}, Result}, [{port,Port}|Stack]) ->
+    {["TCP listener start ERROR\n",
+      io_lib:format("Port: ~p~n"
+                    "Return: ~p~n", [Port,Result])
+     ],
+     Stack};
+
+ssh_dbg_format(tcp, {call, {?MODULE,accept, [ListenSocket, _AcceptTimeout, _Options]}}, Stack) ->
+    {skip, [{lsock,ListenSocket}|Stack]};
+ssh_dbg_format(tcp, {return_from, {?MODULE,accept,3}, {ok,Sock}}, [{lsock,ListenSocket}|Stack]) ->
+    {["TCP accept\n",
+      io_lib:format("ListenSock: ~p~n"
+                    "New Socket: ~p~n", [ListenSocket,Sock])
+     ], Stack};
+ssh_dbg_format(tcp, {return_from, {?MODULE,accept,3}, {error,timeout}}, [{lsock,_ListenSocket}|Stack]) ->
+    {skip, Stack};
+ssh_dbg_format(tcp, {return_from, {?MODULE,accept,3}, Return}, [{lsock,ListenSocket}|Stack]) ->
+    {["TCP accept returned\n",
+      io_lib:format("ListenSock: ~p~n"
+                    "Return: ~p~n", [ListenSocket,Return])
+     ], Stack}.
+
+ssh_dbg_format(tcp, {call, {?MODULE,close, [Socket, _Options]}}) ->
+    ["TCP close listen socket\n",
+     io_lib:format("Socket: ~p~n", [Socket])];
+ssh_dbg_format(tcp, {return_from, {?MODULE,close,2}, _Return}) ->
+    skip;
 
-ssh_dbg_format(connections, {call, {?MODULE,acceptor_init,
-                                    [_Parent, Port, Address, _Opts, _AcceptTimeout]}}) ->
-    [io_lib:format("Starting LISTENER on ~s:~p\n", [ntoa(Address),Port])
+ssh_dbg_format(connections, {call, {?MODULE,acceptor_init, [_Parent, _SysSup, Address, _Opts]}}) ->
+    [io_lib:format("Starting LISTENER on ~s\n", [ssh_lib:format_address(Address)])
     ];
-ssh_dbg_format(connections, {return_from, {?MODULE,acceptor_init,5}, _Ret}) ->
+ssh_dbg_format(connections, {return_from, {?MODULE,acceptor_init,4}, _Ret}) ->
     skip;
 
-ssh_dbg_format(connections, {call, {?MODULE,handle_connection,[_,_,_,_,_]}}) ->
+ssh_dbg_format(connections, {call, {?MODULE,handle_connection,[_Address,_Port,_Options,_Sock]}}) ->
     skip;
-ssh_dbg_format(connections, {return_from, {?MODULE,handle_connection,5}, {error,Error}}) ->
+ssh_dbg_format(connections, {return_from, {?MODULE,handle_connection,4}, {error,Error}}) ->
     ["Starting connection to server failed:\n",
      io_lib:format("Error = ~p", [Error])
     ].
-
-
-
-ntoa(A) ->
-    try inet:ntoa(A)
-    catch
-        _:_ when is_list(A) -> A;
-        _:_ -> io_lib:format('~p',[A])
-    end.
-            
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_app.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_app.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_app.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -20,16 +20,132 @@
 
 %%
 
-%% Purpose : Application master for SSH.
+%%%=========================================================================
+%%% Purpose : Application master and top supervisors for SSH.
+%%%
+%%%  -----> ssh_sup -----+-----> sshc_sup --+--> "connection sup" (etc)
+%%%                      |                  |
+%%%                      |                  +--> "connection sup" (etc)
+%%%                      |                  :
+%%%                      |                  +--> "connection sup" (etc)
+%%%                      |
+%%%                      +-----> sshc_sup --+--> "system sup" (etc)
+%%%                                         |
+%%%                                         +--> "system sup" (etc)
+%%%                                         :
+%%%                                         +--> "system sup" (etc)
 
 -module(ssh_app).
 
 -behaviour(application).
+-behaviour(supervisor).
 
+%% 'application' export:
 -export([start/2, stop/1]).
 
+%% 'supervisor' export:
+-export([init/1]).
+
+
+%%%=========================================================================
+%%%  Application callback
+%%%=========================================================================
 start(_Type, _State) ->
-    supervisor:start_link({local, ssh_sup}, ssh_sup, []).
+    supervisor:start_link({local,ssh_sup}, ?MODULE, [ssh_sup]).
 
 stop(_State) ->
     ok.
+
+%%%=========================================================================
+%%%  Supervisor callback
+%%%=========================================================================
+init([ssh_sup]) ->
+    add_logger_filter(),
+    SupFlags = #{strategy  => one_for_one,
+                 intensity =>   10,
+                 period    => 3600
+                },
+    ChildSpecs = [#{id       => SupName,
+                    start    => {supervisor, start_link,
+                                 [{local,SupName}, ?MODULE, [sshX_sup]]},
+                    type     => supervisor}
+                  || SupName <- [sshd_sup, sshc_sup]
+                 ],
+    {ok, {SupFlags,ChildSpecs}};
+
+init([sshX_sup]) ->
+    SupFlags = #{strategy  => one_for_one,
+                 intensity =>   10,
+                 period    => 3600
+                },
+    ChildSpecs = [],
+    {ok, {SupFlags,ChildSpecs}}.
+
+
+%%%================================================================
+add_logger_filter() ->
+    DefAct = application:get_env(ssh, default_filter, rm),
+    DefF = start_link,
+    ModulesActions =
+        lists:map(fun(M) when is_atom(M) ->
+                          {M,{DefF,DefAct}};
+
+                     ({M,Act}) when is_atom(M),
+                                    (Act == rm orelse
+                                     Act == filter) ->
+                          {M,{DefF,Act}};
+
+                     ({M,F}) when is_atom(M), is_atom(F) ->
+                          {M,{F,DefAct}};
+
+                     ({M,F,Act}) when is_atom(M), is_atom(F),
+                                      (Act == rm orelse
+                                       Act == filter) ->
+                          {M,{F,Act}}
+                  end, application:get_env(ssh, filter_modules, [])),
+    logger:add_primary_filter(ssh_filter, {fun ssh_filter/2, ModulesActions}).
+
+
+ssh_filter(Ev = #{msg := {report, R=#{report := Rep}}},
+           ModulesActions = [_|_]) when is_list(Rep) ->
+    %% io:format("Ev = ~p~n", [Ev]),
+    try
+        Ev#{msg := {report, R#{report := remove_sensitive(Rep, ModulesActions)}}}
+    catch
+        throw:{ssh_filter_return,Ret} -> 
+            %% io:format("ssh_filter returns ~p~n", [Ret]),
+            Ret;
+        _C:_E ->
+            %% io:format("*** ~p ~p~n", [_C,_E]),
+            stop
+    end;
+ssh_filter(OtherEv, _) ->
+    %% io:format("OtherEv = ~p~n", [OtherEv]),
+    OtherEv.
+
+
+remove_sensitive(L, ModActs) when is_list(L) -> rs(L, ModActs);
+remove_sensitive(_, _) -> throw({ssh_filter_return,ignore}).
+
+
+rs([{K,V0}|T], ModActs) when is_list(V0) ->
+    case proplists:get_value(mfargs, V0) of
+        {M,F,A} ->
+            MFA1 = filter(proplists:get_value(M,ModActs), {M,F,A}),
+            V = lists:keyreplace(mfargs, 1, V0, {mfargs,MFA1}),
+            [{K,V} | T];
+        _ ->
+            [{K,V0} | rs(T,ModActs)]
+    end;
+
+rs([H|T], ModActs) ->
+    [H | rs(T,ModActs)];
+
+rs(Other, _) ->
+    Other.
+
+
+filter({F,Act}, {M,F,A}) -> {M, F, ssh_options:no_sensitive(Act,A)};
+filter(stop, _) -> throw({ssh_filter_return,stop});
+filter(_, _) -> throw({ssh_filter_return,ignore}).
+
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh.app.src
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh.app.src
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh.app.src
@@ -19,16 +19,16 @@
 	     ssh_channel,
 	     ssh_connection,
 	     ssh_connection_handler,
-	     ssh_connection_sup,
-             ssh_controller,
+             ssh_fsm_kexinit,
+             ssh_fsm_userauth_client,
+             ssh_fsm_userauth_server,
 	     ssh_daemon_channel,
 	     ssh_dbg,
+             ssh_lib,
 	     ssh_shell,
-	     sshc_sup,
-	     sshd_sup,
-	     ssh_file,
 	     ssh_io,
 	     ssh_info,
+	     ssh_file,
 	     ssh_no_io,
 	     ssh_server_channel,
              ssh_server_key_api,
@@ -36,8 +36,7 @@
 	     ssh_sftpd,
 	     ssh_sftpd_file,
 	     ssh_sftpd_file_api,
-	     ssh_subsystem_sup,
-	     ssh_sup,
+	     ssh_connection_sup,
              ssh_tcpip_forward_client,
              ssh_tcpip_forward_srv,
              ssh_tcpip_forward_acceptor_sup,
@@ -49,19 +48,20 @@
   {applications, [kernel, stdlib, crypto, public_key]},
   {env, [{filter_modules, [
                            ssh_acceptor_sup,
-                           ssh_acceptor,filter,
+                           ssh_acceptor,
                            ssh_channel_sup,
+                           ssh_connection_handler,
                            ssh_connection_sup,
-                           ssh_subsystem_sup,
                            ssh_system_sup
                           ]},
          {default_filter, rm} %% rm | filter
         ]},
   {mod, {ssh_app, []}},
   {runtime_dependencies, [
-			  "crypto-4.6.4",
-			  "erts-9.0",
-			  "kernel-5.3",
+			  "crypto-5.0",
+			  "erts-14.0",
+			  "kernel-9.0",
 			  "public_key-1.6.1",
-			  "stdlib-3.4.1"
+                          "stdlib-5.0","stdlib-5.0",
+                          "runtime_tools-1.15.1"
 			 ]}]}.
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_connect.hrl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_connect.hrl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_connect.hrl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -25,7 +25,6 @@
 -define(DEFAULT_PACKET_SIZE, 65536).
 -define(DEFAULT_WINDOW_SIZE, 10*?DEFAULT_PACKET_SIZE).
 
--define(DEFAULT_TIMEOUT, 5000).
 -define(MAX_PROTO_VERSION, 255).      % Max length of the hello string
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -267,8 +266,8 @@
 	  channel_id_seed,
 	  cli_spec,
 	  options,
+          suggest_window_size,
+          suggest_packet_size,
 	  exec,
-	  system_supervisor,
-	  sub_system_supervisor,
 	  connection_supervisor
 	 }).
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_connection.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_connection.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_connection.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@
 
 -module(ssh_connection).
 
+-include_lib("kernel/include/logger.hrl").
+
 -include("ssh.hrl").
 -include("ssh_connect.hrl").
 -include("ssh_transport.hrl").
@@ -177,7 +179,7 @@
                         Command :: string()
                        } .
 
-%%% This function is soley to convince all
+%%% This function is solely to convince all
 %%% checks that the type event() exists...
 -export([dummy/1]).
 -spec dummy(event()) -> false.
@@ -199,13 +201,13 @@ dummy(_) -> false.
       Result :: {ok, ssh:channel_id()} | {error, reason()} .
 
 session_channel(ConnectionHandler, Timeout) ->
-    session_channel(ConnectionHandler, ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout).
+    session_channel(ConnectionHandler, undefined, undefined, Timeout).
 
 
 -spec session_channel(ConnectionRef, InitialWindowSize, MaxPacketSize, Timeout) -> Result when
       ConnectionRef :: ssh:connection_ref(),
-      InitialWindowSize :: pos_integer(),
-      MaxPacketSize :: pos_integer(),
+      InitialWindowSize :: pos_integer() | undefined,
+      MaxPacketSize :: pos_integer() | undefined,
       Timeout :: timeout(),
       Result :: {ok, ssh:channel_id()} | {error, reason()} .
 
@@ -219,7 +221,7 @@ session_channel(ConnectionHandler, Initi
 %% Description: Opens a channel for the given type.
 %% --------------------------------------------------------------------
 open_channel(ConnectionHandler, Type, ChanData, Timeout) ->
-    open_channel(ConnectionHandler, Type, ChanData, ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout).
+    open_channel(ConnectionHandler, Type, ChanData, undefined, undefined, Timeout).
 
 open_channel(ConnectionHandler, Type, ChanData, InitialWindowSize, MaxPacketSize, Timeout) ->
     case ssh_connection_handler:open_channel(ConnectionHandler, Type, ChanData,
@@ -404,7 +406,7 @@ ptty_alloc(ConnectionHandler, Channel, O
 	   ).
 
 %%--------------------------------------------------------------------
-%% Not yet officialy supported! The following functions are part of the
+%% Not yet officially supported! The following functions are part of the
 %% initial contributed ssh application. They are untested. Do we want them?
 %% Should they be documented and tested?
 %%--------------------------------------------------------------------
@@ -468,23 +470,50 @@ channel_data(ChannelId, DataType, Data0,
 %%% Replies {Reply, UpdatedConnection}
 %%%
 
+handle_msg(#ssh_msg_disconnect{code = Code, description = Description}, Connection, _, _SSH) ->
+    {disconnect, {Code, Description}, handle_stop(Connection)};
+
+handle_msg(Msg, Connection, server, Ssh = #ssh{authenticated = false}) ->
+    %% See RFC4252 6.
+    %% Message numbers of 80 and higher are reserved for protocols running
+    %% after this authentication protocol, so receiving one of them before
+    %% authentication is complete is an error, to which the server MUST
+    %% respond by disconnecting, preferably with a proper disconnect message
+    %% sent to ease troubleshooting.
+    MsgFun = fun(M) ->
+                     io_lib:format("Connection terminated. Unexpected message for unauthenticated user."
+                                   " Message:  ~w", [M],
+                                   [{chars_limit, ssh_lib:max_log_len(Ssh)}])
+             end,
+    ?LOG_DEBUG(MsgFun, [Msg]),
+    {disconnect, {?SSH_DISCONNECT_PROTOCOL_ERROR, "Connection refused"}, handle_stop(Connection)};
+
 handle_msg(#ssh_msg_channel_open_confirmation{recipient_channel = ChannelId, 
 					      sender_channel = RemoteId,
 					      initial_window_size = WindowSz,
 					      maximum_packet_size = PacketSz}, 
 	   #connection{channel_cache = Cache} = Connection0, _, _SSH) ->
     
-    #channel{remote_id = undefined} = Channel =
+    #channel{remote_id = undefined, user = U} = Channel =
 	ssh_client_channel:cache_lookup(Cache, ChannelId), 
     
-    ssh_client_channel:cache_update(Cache, Channel#channel{
-				     remote_id = RemoteId,
-				     recv_packet_size = max(32768, % rfc4254/5.2
-							    min(PacketSz, Channel#channel.recv_packet_size)
-							   ),
-				     send_window_size = WindowSz,
-				     send_packet_size = PacketSz}),
-    reply_msg(Channel, Connection0, {open, ChannelId});
+    if U /= undefined ->
+            ssh_client_channel:cache_update(Cache, Channel#channel{
+                                             remote_id = RemoteId,
+                                             recv_packet_size = max(32768, % rfc4254/5.2
+                                                                    min(PacketSz, Channel#channel.recv_packet_size)
+                                                                   ),
+                                             send_window_size = WindowSz,
+                                             send_packet_size = PacketSz}),
+            reply_msg(Channel, Connection0, {open, ChannelId});
+        true ->
+            %% There is no user process so nobody cares about the channel
+            %% close it and remove from the cache, reply from the peer will be
+            %% ignored
+            CloseMsg = channel_close_msg(RemoteId),
+            ssh_client_channel:cache_delete(Cache, ChannelId),
+            {[{connection_reply, CloseMsg}], Connection0}
+    end;
  
 handle_msg(#ssh_msg_channel_open_failure{recipient_channel = ChannelId,
 					 reason = Reason,
@@ -533,6 +562,10 @@ handle_msg(#ssh_msg_channel_close{recipi
 		{Replies, Connection};
 
 	    undefined ->
+                %% This may happen among other reasons
+                %% - we sent 'channel-close' %% and the peer failed to respond in time
+                %% - we tried to open a channel but the handler died prematurely
+                %%    and the channel entry was removed from the cache
 		{[], Connection0}
 	end;
 
@@ -548,21 +581,23 @@ handle_msg(#ssh_msg_channel_extended_dat
     channel_data_reply_msg(ChannelId, Connection, DataType, Data);
 
 handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId,
-					  bytes_to_add = Add}, 
+					  bytes_to_add = Add},
 	   #connection{channel_cache = Cache} = Connection, _, _SSH) ->
-    #channel{send_window_size = Size, remote_id = RemoteId} = 
-	Channel0 = ssh_client_channel:cache_lookup(Cache, ChannelId), 
-    
-    {SendList, Channel} =  %% TODO: Datatype 0 ?
-	update_send_window(Channel0#channel{send_window_size = Size + Add},
-			   0, undefined, Connection),
-    
-    Replies = lists:map(fun({Type, Data}) -> 
-				{connection_reply, channel_data_msg(RemoteId, Type, Data)}
-			end, SendList),
-    FlowCtrlMsgs = flow_control(Channel, Cache),
-    {Replies ++ FlowCtrlMsgs, Connection};
-
+    case ssh_client_channel:cache_lookup(Cache, ChannelId) of
+        Channel0 = #channel{send_window_size = Size,
+                            remote_id = RemoteId} ->
+            {SendList, Channel} =  %% TODO: Datatype 0 ?
+                update_send_window(Channel0#channel{send_window_size = Size + Add},
+                                   0, undefined, Connection),
+            Replies = lists:map(fun({Type, Data}) ->
+                                        {connection_reply,
+                                         channel_data_msg(RemoteId, Type, Data)}
+                                end, SendList),
+            FlowCtrlMsgs = flow_control(Channel, Cache),
+            {Replies ++ FlowCtrlMsgs, Connection};
+        undefined ->
+            {[], Connection}
+    end;
 handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type,
 				 sender_channel = RemoteId,
 				 initial_window_size = WindowSz,
@@ -603,8 +638,10 @@ handle_msg(#ssh_msg_channel_open{channel
                                 },
            #connection{channel_cache = Cache,
                        channel_id_seed = ChId,
+                       suggest_window_size = WinSz,
+                       suggest_packet_size = PktSz,
                        options = Options,
-                       sub_system_supervisor = SubSysSup
+                       connection_supervisor = ConnectionSup
                       } = C,
 	   client, _SSH) ->
     {ReplyMsg, NextChId} =
@@ -612,7 +649,7 @@ handle_msg(#ssh_msg_channel_open{channel
             {ok, {ConnectToHost,ConnectToPort}} ->
                 case gen_tcp:connect(ConnectToHost, ConnectToPort, [{active,false}, binary]) of
                     {ok,Sock} ->
-                        {ok,Pid} = ssh_subsystem_sup:start_channel(client, SubSysSup, self(),
+                        {ok,Pid} = ssh_connection_sup:start_channel(client, ConnectionSup, self(),
                                                                    ssh_tcpip_forward_client, ChId,
                                                                    [Sock], undefined, Options),
                         ssh_client_channel:cache_update(Cache,
@@ -621,17 +658,15 @@ handle_msg(#ssh_msg_channel_open{channel
                                                                  local_id = ChId,
                                                                  remote_id = RemoteId,
                                                                  user = Pid,
-                                                                 recv_window_size = ?DEFAULT_WINDOW_SIZE,
-                                                                 recv_packet_size = ?DEFAULT_PACKET_SIZE,
+                                                                 recv_window_size = WinSz,
+                                                                 recv_packet_size = PktSz,
                                                                  send_window_size = WindowSize,
                                                                  send_packet_size = PacketSize,
                                                                  send_buf = queue:new()
                                                                 }),
                         gen_tcp:controlling_process(Sock, Pid),
                         inet:setopts(Sock, [{active,once}]),
-                        {channel_open_confirmation_msg(RemoteId, ChId,
-                                                       ?DEFAULT_WINDOW_SIZE, 
-                                                       ?DEFAULT_PACKET_SIZE),
+                        {channel_open_confirmation_msg(RemoteId, ChId, WinSz, PktSz),
                          ChId + 1};
 
                     {error,Error} ->
@@ -661,8 +696,10 @@ handle_msg(#ssh_msg_channel_open{channel
                                 }, 
 	   #connection{channel_cache = Cache,
                        channel_id_seed = ChId,
+                       suggest_window_size = WinSz,
+                       suggest_packet_size = PktSz,
                        options = Options,
-                       sub_system_supervisor = SubSysSup
+                       connection_supervisor = ConnectionSup
                       } = C,
 	   server, _SSH) ->
     {ReplyMsg, NextChId} =
@@ -678,7 +715,7 @@ handle_msg(#ssh_msg_channel_open{channel
                 case gen_tcp:connect(binary_to_list(HostToConnect), PortToConnect,
                                      [{active,false}, binary]) of
                     {ok,Sock} ->
-                        {ok,Pid} = ssh_subsystem_sup:start_channel(server, SubSysSup, self(),
+                        {ok,Pid} = ssh_connection_sup:start_channel(server, ConnectionSup, self(),
                                                                    ssh_tcpip_forward_srv, ChId,
                                                                    [Sock], undefined, Options),
                         ssh_client_channel:cache_update(Cache,
@@ -687,8 +724,8 @@ handle_msg(#ssh_msg_channel_open{channel
                                                                  local_id = ChId,
                                                                  remote_id = RemoteId,
                                                                  user = Pid,
-                                                                 recv_window_size = ?DEFAULT_WINDOW_SIZE,
-                                                                 recv_packet_size = ?DEFAULT_PACKET_SIZE,
+                                                                 recv_window_size = WinSz,
+                                                                 recv_packet_size = PktSz,
                                                                  send_window_size = WindowSize,
                                                                  send_packet_size = PacketSize,
                                                                  send_buf = queue:new()
@@ -696,9 +733,7 @@ handle_msg(#ssh_msg_channel_open{channel
                         gen_tcp:controlling_process(Sock, Pid),
                         inet:setopts(Sock, [{active,once}]),
 
-                        {channel_open_confirmation_msg(RemoteId, ChId,
-                                                       ?DEFAULT_WINDOW_SIZE, 
-                                                       ?DEFAULT_PACKET_SIZE),
+                        {channel_open_confirmation_msg(RemoteId, ChId, WinSz, PktSz),
                          ChId + 1};
 
                     {error,Error} ->
@@ -739,21 +774,35 @@ handle_msg(#ssh_msg_channel_request{reci
 handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
 				    request_type = "exit-signal",
 				    want_reply = false,
-				    data = Data},  
+				    data = Data},
            #connection{channel_cache = Cache} = Connection0, _, _SSH) ->
     <<?DEC_BIN(SigName, _SigLen),
-      ?BOOLEAN(_Core), 
+      ?BOOLEAN(_Core),
       ?DEC_BIN(Err, _ErrLen),
       ?DEC_BIN(Lang, _LangLen)>> = Data,
-    Channel = ssh_client_channel:cache_lookup(Cache, ChannelId),
-    RemoteId =  Channel#channel.remote_id,
-    {Reply, Connection} =  reply_msg(Channel, Connection0, 
-				     {exit_signal, ChannelId,
-				      binary_to_list(SigName),
-				      binary_to_list(Err),
-				      binary_to_list(Lang)}),
-    CloseMsg = channel_close_msg(RemoteId),
-    {[{connection_reply, CloseMsg}|Reply], Connection};
+    case ssh_client_channel:cache_lookup(Cache, ChannelId) of
+        #channel{remote_id = RemoteId, sent_close = SentClose} = Channel ->
+            {Reply, Connection} =  reply_msg(Channel, Connection0,
+                                             {exit_signal, ChannelId,
+                                              binary_to_list(SigName),
+                                              binary_to_list(Err),
+                                              binary_to_list(Lang)}),
+            %% Send 'channel-close' only if it has not been sent yet
+            %% by e.g. our side also closing the channel or going down
+            %% and(!) update the cache
+            %% so that the 'channel-close' is not sent twice
+            if not SentClose ->
+                    CloseMsg = channel_close_msg(RemoteId),
+                    ssh_client_channel:cache_update(Cache,
+                                            Channel#channel{sent_close = true}),
+                    {[{connection_reply, CloseMsg}|Reply], Connection};
+                true ->
+                    {Reply, Connection}
+            end;
+        _ ->
+            %% Channel already closed by peer
+            {[], Connection0}
+    end;
 
 handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
 				    request_type = "xon-xoff",
@@ -811,26 +860,20 @@ handle_msg(#ssh_msg_channel_request{reci
 				    request_type = "pty-req",
 				    want_reply = WantReply,
 				    data = Data},
-	   Connection, server, SSH) ->
+	   Connection, server, _SSH) ->
     <<?DEC_BIN(BTermName,_TermLen),
       ?UINT32(Width),?UINT32(Height),
       ?UINT32(PixWidth), ?UINT32(PixHeight),
       Modes/binary>> = Data,
     TermName = binary_to_list(BTermName),
     PtyOpts0 = decode_pty_opts(Modes),
-    PtyOpts = case SSH#ssh.c_version of
-                  "SSH-2.0-PuTTY"++_ ->
-                      %% If - peer client is PuTTY
-                      %%    - it asked for pty
+    PtyOpts = case proplists:get_value(onlcr, PtyOpts0, undefined) of
+                  undefined ->
+                      %% If - peer client asked for pty
                       %%    - did not tell if LF->CRLF expansion is wanted
                       %% then
                       %%    - do LF->CRLF expansion
-                      case  proplists:get_value(onlcr, PtyOpts0, undefined) of
-                          undefined ->
-                              [{onlcr,1} | PtyOpts0];
-                          _ ->
-                              PtyOpts0
-                      end;
+                      [{onlcr,1} | PtyOpts0];
                   _ ->
                       PtyOpts0
               end,
@@ -918,9 +961,8 @@ handle_msg(#ssh_msg_global_request{name
             {[{connection_reply, request_failure_msg()}], Connection};
 
         true ->
-            Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-            SubSysSup = proplists:get_value(subsystem_sup,  Sups),
-            FwdSup = ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
+            ConnectionSup = ?GET_INTERNAL_OPT(connection_sup, Opts),
+            FwdSup = ssh_connection_sup:tcpip_fwd_supervisor(ConnectionSup),
             ConnPid = self(),
             case ssh_tcpip_forward_acceptor:supervised_start(FwdSup,
                                                              {ListenAddrStr, ListenPort},
@@ -973,12 +1015,7 @@ handle_msg(#ssh_msg_request_success{data
 	   #connection{requests = [{_, From, Fun} | Rest]} = Connection0, _, _SSH) ->
     Connection = Fun({success,Data}, Connection0),
     {[{channel_request_reply, From, {success, Data}}],
-     Connection#connection{requests = Rest}};
-
-handle_msg(#ssh_msg_disconnect{code = Code,
-			       description = Description},
-	   Connection, _, _SSH) ->
-    {disconnect, {Code, Description}, handle_stop(Connection)}.
+     Connection#connection{requests = Rest}}.
 
 
 %%%----------------------------------------------------------------
@@ -1102,16 +1139,19 @@ encode_ip(Addr) when is_list(Addr) ->
 %%% of "session" typ is handled
 %%%
 setup_session(#connection{channel_cache = Cache,
-                          channel_id_seed = NewChannelID
+                          channel_id_seed = NewChannelID,
+                          suggest_window_size = WinSz,
+                          suggest_packet_size = PktSz
 			 } = C,
-	      RemoteId, Type, WindowSize, PacketSize) ->
+	      RemoteId, Type, WindowSize, PacketSize) when is_integer(WinSz),
+                                                           is_integer(PktSz) ->
     NextChannelID = NewChannelID + 1,
     Channel =
         #channel{type = Type,
                  sys = "ssh",
                  local_id = NewChannelID,
-                 recv_window_size = ?DEFAULT_WINDOW_SIZE,
-                 recv_packet_size = ?DEFAULT_PACKET_SIZE,
+                 recv_window_size = WinSz,
+                 recv_packet_size = PktSz,
                  send_window_size = WindowSize,
                  send_packet_size = PacketSize,
                  send_buf = queue:new(),
@@ -1119,9 +1159,9 @@ setup_session(#connection{channel_cache
                 },
     ssh_client_channel:cache_update(Cache, Channel),
     OpenConfMsg = channel_open_confirmation_msg(RemoteId, NewChannelID,
-						?DEFAULT_WINDOW_SIZE, 
-						?DEFAULT_PACKET_SIZE),
-    Reply = {connection_reply, OpenConfMsg},
+						WinSz, 
+						PktSz),
+                Reply = {connection_reply, OpenConfMsg},
     {[Reply], C#connection{channel_id_seed = NextChannelID}}.
 
 
@@ -1131,22 +1171,22 @@ setup_session(#connection{channel_cache
 start_cli(#connection{options = Options, 
 		      cli_spec = CliSpec,
 		      exec = Exec,
-		      sub_system_supervisor = SubSysSup}, ChannelId) ->
+		      connection_supervisor = ConnectionSup}, ChannelId) ->
     case CliSpec of
         no_cli ->
             {error, cli_disabled};
         {CbModule, Args} ->
-            ssh_subsystem_sup:start_channel(server, SubSysSup, self(), CbModule, ChannelId, Args, Exec, Options)
+            ssh_connection_sup:start_channel(server, ConnectionSup, self(), CbModule, ChannelId, Args, Exec, Options)
     end.
 
 
 start_subsystem(BinName, #connection{options = Options,
-                                     sub_system_supervisor = SubSysSup},
+                                     connection_supervisor = ConnectionSup},
 	       #channel{local_id = ChannelId}, _ReplyMsg) ->
     Name = binary_to_list(BinName),
     case check_subsystem(Name, Options) of
 	{Callback, Opts} when is_atom(Callback), Callback =/= none ->
-            ssh_subsystem_sup:start_channel(server, SubSysSup, self(), Callback, ChannelId, Opts, undefined, Options);
+            ssh_connection_sup:start_channel(server, ConnectionSup, self(), Callback, ChannelId, Opts, undefined, Options);
         {none, _} ->
             {error, bad_subsystem};
 	{_, _} ->
@@ -1204,7 +1244,7 @@ get_window(#channel{send_buf = Buffer,
 		   } = Channel, Acc0) ->
     case queue:out(Buffer) of
 	{{value, {_, Data} = Msg}, NewBuffer} ->
-	    case handle_send_window(Msg, size(Data), PacketSize, WindowSize0, Acc0) of		
+	    case handle_send_window(Msg, byte_size(Data), PacketSize, WindowSize0, Acc0) of
 		{WindowSize, Acc, {_, <<>>}} ->
 		    {lists:reverse(Acc), Channel#channel{send_window_size = WindowSize,
 							 send_buf = NewBuffer}};
@@ -1524,7 +1564,7 @@ handle_cli_msg(C0, ChId, Reply0) ->
 channel_data_reply_msg(ChannelId, Connection, DataType, Data) ->
     case ssh_client_channel:cache_lookup(Connection#connection.channel_cache, ChannelId) of
 	#channel{recv_window_size = Size} = Channel ->
-	    WantedSize = Size - size(Data),
+	    WantedSize = Size - byte_size(Data),
 	    ssh_client_channel:cache_update(Connection#connection.channel_cache, 
                                      Channel#channel{recv_window_size = WantedSize}),
             reply_msg(Channel, Connection, {data, ChannelId, DataType, Data});
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_connection_handler.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_connection_handler.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_connection_handler.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -34,27 +34,34 @@
 -include("ssh_transport.hrl").
 -include("ssh_auth.hrl").
 -include("ssh_connect.hrl").
+-include("ssh_fsm.hrl").
 
 %%====================================================================
 %%% Exports
 %%====================================================================
 
 %%% Start and stop
--export([start_link/3,
+-export([start_link/3, start_link/4,
+         takeover/4,
 	 stop/1
 	]).
 
 %%% Internal application API
--export([start_connection/4,
-         available_hkey_algorithms/2,
+-export([available_hkey_algorithms/2,
 	 open_channel/6,
          start_channel/5,
+         handshake/2,
          handle_direct_tcpip/6,
 	 request/6, request/7,
-	 reply_request/3, 
+	 reply_request/3,
          global_request/5,
+         handle_ssh_msg_ext_info/2,
 	 send/5,
+         send_bytes/2,
+         send_msg/2,
 	 send_eof/2,
+         send_disconnect/6,
+         send_disconnect/7,
          store/3,
          retrieve/2,
 	 info/1, info/2,
@@ -67,16 +74,12 @@
          prohibited_sock_option/1
 	]).
 
--type connection_ref() :: ssh:connection_ref().
--type channel_id()     :: ssh:channel_id().
-
 %%% Behaviour callbacks
 -export([init/1, callback_mode/0, handle_event/4, terminate/3,
 	 format_status/2, code_change/4]).
 
 %%% Exports not intended to be used :). They are used for spawning and tests
--export([init_connection_handler/3,	   % proc_lib:spawn needs this
-	 init_ssh_record/3,		   % Export of this internal function
+-export([init_ssh_record/3,		   % Export of this internal function
 					   % intended for low-level protocol test suites
 	 renegotiate/1, alg/1 % Export intended for test cases
 	]).
@@ -87,31 +90,54 @@
          ssh_dbg_format/2, ssh_dbg_format/3]).
 
 
--define(send_disconnect(Code, DetailedText, StateName, State),
-        send_disconnect(Code, DetailedText, ?MODULE, ?LINE, StateName, State)).
-
--define(send_disconnect(Code, Reason, DetailedText, StateName, State),
-        send_disconnect(Code, Reason, DetailedText, ?MODULE, ?LINE, StateName, State)).
-
 -define(call_disconnectfun_and_log_cond(LogMsg, DetailedText, StateName, D),
         call_disconnectfun_and_log_cond(LogMsg, DetailedText, ?MODULE, ?LINE, StateName, D)).
 
 %%====================================================================
 %% Start / stop
 %%====================================================================
-%%--------------------------------------------------------------------
--spec start_link(role(),
-		 gen_tcp:socket(),
-                 internal_options()
-		) -> {ok, pid()}.
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
 start_link(Role, Socket, Options) ->
-    {ok, proc_lib:spawn_opt(?MODULE, 
-                            init_connection_handler, 
-                            [Role, Socket, Options],
-                            [link, {message_queue_data,off_heap}]
-                           )}.
+    start_link(Role, undefined, Socket, Options).
+
+start_link(Role, Id, Socket, Options) ->
+    case gen_statem:start_link(?MODULE,
+                               [Role, Socket, Options],
+                               [{spawn_opt, [{message_queue_data,off_heap}]}]) of
+
+        {ok, Pid} when Id =/= undefined ->
+            %% Announce the ConnectionRef to the system supervisor so it could
+            %%   1) initiate the socket handover, and
+            %%   2) be returned to whoever called for example ssh:connect; the Pid
+            %%      returned from this function is "consumed" by the connection
+            %%      supervisor.
+            ?GET_INTERNAL_OPT(user_pid,Options) ! {new_connection_ref, Id, Pid},
+            {ok, Pid};
 
+        Others ->
+            Others
+    end.
+
+takeover(ConnPid, Role, Socket, Options) ->
+    case Role of
+        client ->
+            group_leader(group_leader(), ConnPid);
+        _ ->
+            ok
+    end,
+    {_, Callback, _} = ?GET_OPT(transport, Options),
+    case Callback:controlling_process(Socket, ConnPid) of
+        ok ->
+            Ref = erlang:monitor(process, ConnPid),
+            gen_statem:cast(ConnPid, socket_control),
+            NegTimeout = ?GET_INTERNAL_OPT(negotiation_timeout,
+                                           Options,
+                                           ?GET_OPT(negotiation_timeout, Options)
+                                          ),
+            handshake(ConnPid, Role, Ref, NegTimeout);
+        {error, Reason}	->
+            {error, Reason}
+    end.
 
 %%--------------------------------------------------------------------
 -spec stop(connection_ref()
@@ -130,46 +156,6 @@ stop(ConnectionHandler)->
 %%====================================================================
 
 %%--------------------------------------------------------------------
--spec start_connection(role(),
-		       gen_tcp:socket(),
-                       internal_options(),
-		       timeout()
-		      ) -> {ok, connection_ref()} | {error, term()}.
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-start_connection(Role, Socket, Options, Timeout) ->
-    try
-        case Role of
-            client ->
-                ChildPid = start_the_connection_child(self(), Role, Socket, Options),
-                handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout);
-            server ->
-                case ?GET_OPT(parallel_login, Options) of
-                    true ->
-                        HandshakerPid =
-                            spawn_link(fun() ->
-                                               process_flag(trap_exit, true),
-                                               receive
-                                                   {do_handshake, Pid} ->
-                                                       handshake(Pid, erlang:monitor(process,Pid), Timeout)
-                                               after Timeout ->
-                                                       {error, timeout2}
-                                               end
-                                       end),
-                        ChildPid = start_the_connection_child(HandshakerPid, Role, Socket, Options),
-                        HandshakerPid ! {do_handshake, ChildPid};
-                    false ->
-                        ChildPid = start_the_connection_child(self(), Role, Socket, Options),
-                        handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
-                end
-        end
-    catch
-	exit:{noproc, _} ->
-	    {error, ssh_not_started};
-	_:Error ->
-	    {error, Error}
-    end.
-
-%%--------------------------------------------------------------------
 %%% Some other module has decided to disconnect.
 
 -spec disconnect(Code::integer(), Details::iodata(),
@@ -188,18 +174,18 @@ disconnect(Code, DetailedText, Module, L
 -spec open_channel(connection_ref(), 
 		   string(),
 		   iodata(),
-		   pos_integer(),
-		   pos_integer(),
+		   pos_integer() | undefined,
+		   pos_integer() | undefined,
 		   timeout()
 		  ) -> {open, channel_id()} | {error, term()}.
-		   
+
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-open_channel(ConnectionHandler, 
+open_channel(ConnectionHandler,
 	     ChannelType, ChannelSpecificData, InitialWindowSize, MaxPacketSize, 
 	     Timeout) ->
     call(ConnectionHandler,
-	 {open, 
-	  self(), 
+	 {open,
+	  self(),
 	  ChannelType, InitialWindowSize, MaxPacketSize, ChannelSpecificData,
 	  Timeout}).
 
@@ -210,8 +196,8 @@ open_channel(ConnectionHandler,
 
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 start_channel(ConnectionHandler, CallbackModule, ChannelId, Args, Exec) ->
-    {ok, {SubSysSup,Role,Opts}} = call(ConnectionHandler, get_misc),
-    ssh_subsystem_sup:start_channel(Role, SubSysSup,
+    {ok, {ConnectionSup,Role,Opts}} = call(ConnectionHandler, get_misc),
+    ssh_connection_sup:start_channel(Role, ConnectionSup,
                                     ConnectionHandler, CallbackModule, ChannelId,
                                     Args, Exec, Opts).
 
@@ -252,7 +238,7 @@ request(ConnectionHandler, ChannelId, Ty
 		    success | failure,
 		    channel_id()
 		   ) -> ok.
-			   
+
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 reply_request(ConnectionHandler, Status, ChannelId) ->
     cast(ConnectionHandler, {reply_request, Status, ChannelId}).
@@ -262,7 +248,7 @@ global_request(ConnectionHandler, Type,
     call(ConnectionHandler, {global_request, Type, Data, Timeout});
 global_request(ConnectionHandler, Type, false, Data, _) ->
     cast(ConnectionHandler, {global_request, Type, Data}).
-    
+
 %%--------------------------------------------------------------------
 -spec send(connection_ref(),
 	   channel_id(),
@@ -355,10 +341,10 @@ close(ConnectionHandler, ChannelId) ->
 %%--------------------------------------------------------------------
 store(ConnectionHandler, Key, Value) ->
     cast(ConnectionHandler, {store,Key,Value}).
-    
+
 retrieve(#connection{options=Opts}, Key) ->
     try ?GET_INTERNAL_OPT(Key, Opts) of
-        Value -> 
+        Value ->
             {ok,Value}
     catch
         error:{badkey,Key} ->
@@ -366,7 +352,7 @@ retrieve(#connection{options=Opts}, Key)
     end;
 retrieve(ConnectionHandler, Key) ->
     call(ConnectionHandler, {retrieve,Key}).
-    
+
 %%--------------------------------------------------------------------
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 set_sock_opts(ConnectionRef, SocketOptions) ->
@@ -412,133 +398,37 @@ alg(ConnectionHandler) ->
     call(ConnectionHandler, get_alg).
 
 %%====================================================================
-%% Internal process state
-%%====================================================================
--record(data, {
-	  starter                               :: pid()
-						 | undefined,
-	  auth_user                             :: string()
-						 | undefined,
-	  connection_state                      :: #connection{}
-						 | undefined,
-	  latest_channel_id         = 0         :: non_neg_integer()
-                                                 | undefined,
-	  transport_protocol                    :: atom()
-                                                 | undefined,	% ex: tcp
-	  transport_cb                          :: atom()
-                                                 | undefined,	% ex: gen_tcp
-	  transport_close_tag                   :: atom()
-                                                 | undefined,	% ex: tcp_closed
-	  ssh_params                            :: #ssh{}
-                                                 | undefined,
-	  socket                                :: gen_tcp:socket()
-                                                 | undefined,
-	  decrypted_data_buffer     = <<>>      :: binary()
-                                                 | undefined,
-	  encrypted_data_buffer     = <<>>      :: binary()
-                                                 | undefined,
-	  aead_data                 = <<>>      :: binary()
-                                                 | undefined,
-	  undecrypted_packet_length             :: undefined | non_neg_integer(),
-	  key_exchange_init_msg                 :: #ssh_msg_kexinit{}
-						 | undefined,
-	  last_size_rekey           = 0         :: non_neg_integer(),
-	  event_queue               = []        :: list(),
-	  inet_initial_recbuf_size              :: pos_integer()
-						 | undefined
-	 }).
-
-%%====================================================================
 %% Intitialisation
 %%====================================================================
-%%--------------------------------------------------------------------
--spec init_connection_handler(role(),
-			      gen_tcp:socket(),
-			      internal_options()
-			     ) -> no_return().
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-init_connection_handler(Role, Socket, Opts) ->
-    case init([Role, Socket, Opts]) of
-        {ok, StartState, D} when Role == server ->
-            process_flag(trap_exit, true),
-            gen_statem:enter_loop(?MODULE,
-                                  [], %%[{debug,[trace,log,statistics,debug]} ], %% []
-                                  StartState,
-                                  D);
-
-        {ok, StartState, D0=#data{connection_state=C}} when Role == client ->
-            process_flag(trap_exit, true),
-            Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-            D = D0#data{connection_state = 
-                            C#connection{system_supervisor =     proplists:get_value(system_sup,     Sups),
-                                         sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
-                                         connection_supervisor = proplists:get_value(connection_sup, Sups)
-                                        }},
-            gen_statem:enter_loop(?MODULE,
-                                  [], %%[{debug,[trace,log,statistics,debug]} ], %% []
-                                  StartState,
-                                  D);
-
-        {stop, Error} ->
-            D = try
-                    %% Only servers have supervisorts defined in Opts
-                    Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-                    #connection{system_supervisor =     proplists:get_value(system_sup,     Sups),
-                                sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
-                                connection_supervisor = proplists:get_value(connection_sup, Sups)
-                               }
-                of
-                    C ->
-                        #data{connection_state=C}
-                catch
-                    _:_ ->
-                        #data{connection_state=#connection{}}
-                end,
-            gen_statem:enter_loop(?MODULE,
-                                  [],
-                                  {init_error,Error},
-                                  D#data{socket=Socket})
-    end.
-
-
+init([Role, Socket, Opts]) when Role==client ; Role==server ->
+    process_flag(trap_exit, true),
+    %% ssh_params will be updated after receiving socket_control event
+    %% in wait_for_socket state;
+    D = #data{socket = Socket, ssh_params = #ssh{role = Role, opts = Opts}},
+    {ok, {wait_for_socket, Role}, D}.
 
-init([Role,Socket,Opts]) ->
-    case inet:peername(Socket) of
-        {ok, PeerAddr} ->
-            {Protocol, Callback, CloseTag} = ?GET_OPT(transport, Opts),
-            C = #connection{channel_cache = ssh_client_channel:cache_create(),
-                            channel_id_seed = 0,
-                            requests = [],
-                            options = Opts},
-            D0 = #data{starter = ?GET_INTERNAL_OPT(user_pid, Opts),
-                       connection_state = C,
-                       socket = Socket,
-                       transport_protocol = Protocol,
-                       transport_cb = Callback,
-                       transport_close_tag = CloseTag,
-                       ssh_params = init_ssh_record(Role, Socket, PeerAddr, Opts)
-              },
-            D = case Role of
-                    client ->
-                        D0;
-                    server ->
-                        Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-                        D0#data{connection_state = 
-                                    C#connection{cli_spec = ?GET_OPT(ssh_cli, Opts, {ssh_cli,[?GET_OPT(shell, Opts)]}),
-                                                 exec =     ?GET_OPT(exec,    Opts),
-                                                 system_supervisor =     proplists:get_value(system_sup,     Sups),
-                                                 sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
-                                                 connection_supervisor = proplists:get_value(connection_sup, Sups)
-                                                }}
-                end,
-            {ok, {hello,Role}, D};
-        
-        {error,Error} ->
-            {stop, Error}
+%%%----------------------------------------------------------------
+%%% Connection start and initialization helpers
+init_connection_record(Role, Socket, Opts) ->
+    {WinSz, PktSz} = init_inet_buffers_window(Socket),
+    C = #connection{channel_cache = ssh_client_channel:cache_create(),
+                    channel_id_seed = 0,
+                    suggest_window_size = WinSz,
+                    suggest_packet_size = PktSz,
+                    requests = [],
+                    options = Opts,
+                    connection_supervisor = ?GET_INTERNAL_OPT(connection_sup, Opts)
+                   },
+    case Role of
+        server ->
+            C#connection{cli_spec =
+                             ?GET_OPT(ssh_cli, Opts, {ssh_cli,[?GET_OPT(shell, Opts)]}),
+                         exec =
+                             ?GET_OPT(exec, Opts)};
+        client ->
+            C
     end.
 
-
-
 init_ssh_record(Role, Socket, Opts) ->
     %% Export of this internal function is
     %% intended for low-level protocol test suites
@@ -596,7 +486,46 @@ init_ssh_record(Role, Socket, PeerAddr,
 		  }
     end.
 
+handshake(ConnPid, server, Ref, Timeout) ->
+    receive
+	{ConnPid, ssh_connected} ->
+	    erlang:demonitor(Ref, [flush]),
+	    {ok, ConnPid};
+	{ConnPid, {not_connected, Reason}} ->
+	    erlang:demonitor(Ref, [flush]),
+	    {error, Reason};
+	{'DOWN', Ref, process, ConnPid, {shutdown, Reason}} ->
+	    {error, Reason};
+	{'DOWN', Ref, process, ConnPid, Reason} ->
+	    {error, Reason};
+        {'EXIT',_,Reason} ->
+            stop(ConnPid),
+            {error, {exit,Reason}}
+    after Timeout ->
+	    erlang:demonitor(Ref, [flush]),
+	    ssh_connection_handler:stop(ConnPid),
+	    {error, timeout}
+    end;
+handshake(ConnPid, client, Ref, Timeout) ->
+    receive
+	{ConnPid, ssh_connected} ->
+	    erlang:demonitor(Ref, [flush]),
+	    {ok, ConnPid};
+	{ConnPid, {not_connected, Reason}} ->
+	    erlang:demonitor(Ref, [flush]),
+	    {error, Reason};
+	{'DOWN', Ref, process, ConnPid, {shutdown, Reason}} ->
+	    {error, Reason};
+	{'DOWN', Ref, process, ConnPid, Reason} ->
+	    {error, Reason}
+    after Timeout ->
+	    erlang:demonitor(Ref, [flush]),
+	    ssh_connection_handler:stop(ConnPid),
+	    {error, timeout}
+    end.
 
+handshake(Msg, #data{starter = User}) ->
+    User ! {self(), Msg}.
 
 %%====================================================================
 %% gen_statem callbacks
@@ -625,16 +554,12 @@ init_ssh_record(Role, Socket, PeerAddr,
 %% The state names must fulfill some rules regarding
 %% where the role() and the renegotiate_flag() is placed:
 
--spec role(state_name()) -> role().
-role({_,Role}) -> Role;
-role({_,Role,_}) -> Role.
-
 -spec renegotiation(state_name()) -> boolean().
 renegotiation({_,_,ReNeg}) -> ReNeg == renegotiate;
 renegotiation(_) -> false.
 
 
--define(CONNECTED(StateName), 
+-define(CONNECTED(StateName),
         (element(1,StateName) == connected orelse
          element(1,StateName) == ext_info ) ).
 
@@ -643,51 +568,59 @@ renegotiation(_) -> false.
 		   state_name(),
 		   #data{}
 		  ) -> gen_statem:event_handler_result(state_name()) .
-		   
+
 -define(CONNECTION_MSG(Msg),
         [{next_event, internal, prepare_next_packet},
          {next_event,internal,{conn_msg,Msg}}]).
 
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
 callback_mode() ->
     [handle_event_function,
      state_enter].
 
+%%% ######## {hello, client|server} ####
+%% The very first event that is sent when ssh_connection_handler
+%% becomes owner process for Socket
+handle_event(cast, socket_control, {wait_for_socket, Role},
+             #data{socket = Socket, ssh_params = #ssh{opts = Opts}}) ->
+    case inet:peername(Socket) of
+        {ok, PeerAddr} ->
+            try
+                {Protocol, Callback, CloseTag} = ?GET_OPT(transport, Opts),
+                D = #data{starter = ?GET_INTERNAL_OPT(user_pid, Opts),
+                          socket = Socket,
+                          transport_protocol = Protocol,
+                          transport_cb = Callback,
+                          transport_close_tag = CloseTag,
+                          ssh_params = init_ssh_record(Role, Socket, PeerAddr, Opts),
+                          connection_state = init_connection_record(Role, Socket, Opts)
+                         },
+                NextEvent = {next_event, internal, socket_ready},
+                {next_state, {hello,Role}, D, NextEvent}
+            catch
+                _:{error,Error} -> {stop, {error,Error}};
+                error:Error ->     {stop, {error,Error}}
+            end;
 
-handle_event(_, _Event, {init_error,Error}=StateName, D) ->
-    case Error of
-        enotconn ->
-           %% Handles the abnormal sequence:
-           %%    SYN->
-           %%            <-SYNACK
-           %%    ACK->
-           %%    RST->
-            ?call_disconnectfun_and_log_cond("Protocol Error",
-                                             "TCP connenction to server was prematurely closed by the client",
-                                             StateName, D),
-            {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}};
-
-        OtherError ->
-            {stop, {shutdown,{init,OtherError}}}
+        {error,Error} ->
+            {stop, {shutdown,Error}}
     end;
 
-%%% ######## {hello, client|server} ####
-%% The very first event that is sent when the we are set as controlling process of Socket
-handle_event(_, socket_control, {hello,_}=StateName, #data{ssh_params = Ssh0} = D) ->
+handle_event(internal, socket_ready, {hello,_}=StateName, #data{ssh_params = Ssh0} = D) ->
     VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh0)),
     send_bytes(VsnMsg, D),
-    case inet:getopts(Socket=D#data.socket, [recbuf]) of
-	{ok, [{recbuf,Size}]} ->
+    case inet:getopts(Socket=D#data.socket, [buffer]) of
+	{ok, [{buffer,Size}]} ->
 	    %% Set the socket to the hello text line handling mode:
 	    inet:setopts(Socket, [{packet, line},
 				  {active, once},
 				  % Expecting the version string which might
 				  % be max ?MAX_PROTO_VERSION bytes:
-				  {recbuf, ?MAX_PROTO_VERSION},
+				  {buffer, ?MAX_PROTO_VERSION},
+				  {packet_size, ?MAX_PROTO_VERSION},
 				  {nodelay,true}]),
             Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts, infinity),
-	    {keep_state, D#data{inet_initial_recbuf_size=Size}, [{state_timeout,Time,no_hello_received}] };
+	    {keep_state, D#data{inet_initial_buffer_size=Size}, [{state_timeout,Time,no_hello_received}] };
 
 	Other ->
             ?call_disconnectfun_and_log_cond("Option return", 
@@ -696,39 +629,40 @@ handle_event(_, socket_control, {hello,_
 	    {stop, {shutdown,{unexpected_getopts_return, Other}}}
     end;
 
-handle_event(_, {info_line,Line}, {hello,Role}=StateName, D) ->
-    case Role of
-	client ->
-	    %% The server may send info lines to the client before the version_exchange
-	    %% RFC4253/4.2
-	    inet:setopts(D#data.socket, [{active, once}]),
-	    keep_state_and_data;
-	server ->
-	    %% But the client may NOT send them to the server. Openssh answers with cleartext,
-	    %% and so do we
-	    send_bytes("Protocol mismatch.", D),
-            Msg = io_lib:format("Protocol mismatch in version exchange. Client sent info lines.~n~s",
-                                [ssh_dbg:hex_dump(Line, 64)]),
-            ?call_disconnectfun_and_log_cond("Protocol mismatch.", Msg, StateName, D),
-	    {stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}}
-    end;
+handle_event(internal, {info_line,_Line}, {hello,client}, D) ->
+    %% The server may send info lines to the client before the version_exchange
+    %% RFC4253/4.2
+    inet:setopts(D#data.socket, [{active, once}]),
+    keep_state_and_data;
+
+handle_event(internal, {info_line,Line}, {hello,server}=StateName, D) ->
+    %% But the client may NOT send them to the server. Openssh answers with cleartext,
+    %% and so do we
+    send_bytes("Protocol mismatch.", D),
+    Msg = io_lib:format("Protocol mismatch in version exchange. Client sent info lines.~n~s",
+                        [ssh_dbg:hex_dump(Line, 64)]),
+    ?call_disconnectfun_and_log_cond("Protocol mismatch.", Msg, StateName, D),
+    {stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}};
 
-handle_event(_, {version_exchange,Version}, {hello,Role}, D0) ->
+handle_event(internal, {version_exchange,Version}, {hello,Role}, D0) ->
     {NumVsn, StrVsn} = ssh_transport:handle_hello_version(Version),
     case handle_version(NumVsn, StrVsn, D0#data.ssh_params) of
 	{ok, Ssh1} ->
-	    %% Since the hello part is finnished correctly, we set the
-	    %% socket to the packet handling mode (including recbuf size):
+	    %% Since the hello part is finished correctly, we set the
+	    %% socket to the packet handling mode (including buffer size):
 	    inet:setopts(D0#data.socket, [{packet,0},
 					 {mode,binary},
 					 {active, once},
-					 {recbuf, D0#data.inet_initial_recbuf_size}]),
+					 {buffer, D0#data.inet_initial_buffer_size},
+					 {packet_size, 0}]),
 	    {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
 	    send_bytes(SshPacket, D0),
-	    {next_state, {kexinit,Role,init}, D0#data{ssh_params = Ssh,
-						     key_exchange_init_msg = KeyInitMsg}};
+            D = D0#data{ssh_params = Ssh,
+                        key_exchange_init_msg = KeyInitMsg},
+	    {next_state, {kexinit,Role,init}, D, {change_callback_module, ssh_fsm_kexinit}};
+
 	not_supported ->
-            {Shutdown, D} =  
+            {Shutdown, D} =
                 ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
                                  io_lib:format("Offending version is ~p",[string:chomp(Version)]),
                                  {hello,Role},
@@ -736,403 +670,85 @@ handle_event(_, {version_exchange,Versio
 	    {stop, Shutdown, D}
     end;
 
-handle_event(_, no_hello_received, {hello,_Role}=StateName, D0) ->
+%%% timeout after tcp:connect but then nothing arrives
+handle_event(state_timeout, no_hello_received, {hello,_Role}=StateName, D0 = #data{ssh_params = Ssh0}) ->
+    MsgFun =
+        fun (debug) ->
+                Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts),
+                lists:concat(["No HELLO received within ",ssh_lib:format_time_ms(Time)]);
+            (_) ->
+                ["No HELLO received within hello_timeout"]
+        end,
     {Shutdown, D} =
-        ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, "No HELLO recieved", StateName, D0),
+        ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, ?SELECT_MSG(MsgFun), StateName, D0),
     {stop, Shutdown, D};
-		  
-%%% ######## {kexinit, client|server, init|renegotiate} ####
-
-handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
-	     D = #data{key_exchange_init_msg = OwnKex}) ->
-    Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload),
-    Ssh = case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
-	      {ok, NextKexMsg, Ssh2} when Role==client ->
-		  send_bytes(NextKexMsg, D),
-		  Ssh2;
-	      {ok, Ssh2} when Role==server ->
-		  Ssh2
-	  end,
-    {next_state, {key_exchange,Role,ReNeg}, D#data{ssh_params=Ssh}};
-
-
-%%% ######## {key_exchange, client|server, init|renegotiate} ####
-
-%%%---- diffie-hellman
-handle_event(_, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
-    {ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params),
-    send_bytes(KexdhReply, D),
-    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
-    send_bytes(NewKeys, D),
-    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
-    send_bytes(ExtInfo, D),
-    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
-
-handle_event(_, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
-    {ok, NewKeys, Ssh1} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params),
-    send_bytes(NewKeys, D),
-    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
-    send_bytes(ExtInfo, D),
-    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
-
-%%%---- diffie-hellman group exchange
-handle_event(_, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) ->
-    {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
-    send_bytes(GexGroup, D),
-    Ssh = ssh_transport:parallell_gen_key(Ssh1),
-    {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
-
-handle_event(_, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) ->
-    {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
-    send_bytes(GexGroup, D),
-    Ssh = ssh_transport:parallell_gen_key(Ssh1),
-    {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
-
-handle_event(_, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) ->
-    {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params),
-    send_bytes(KexGexInit, D),
-    {next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}};
-
-%%%---- elliptic curve diffie-hellman
-handle_event(_, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
-    {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params),
-    send_bytes(KexEcdhReply, D),
-    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
-    send_bytes(NewKeys, D),
-    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
-    send_bytes(ExtInfo, D),
-    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
-
-handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
-    {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params),
-    send_bytes(NewKeys, D),
-    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
-    send_bytes(ExtInfo, D),
-    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
-
-
-%%% ######## {key_exchange_dh_gex_init, server, init|renegotiate} ####
-
-handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) ->
-    {ok, KexGexReply, Ssh1} =  ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params),
-    send_bytes(KexGexReply, D),
-    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
-    send_bytes(NewKeys, D),
-    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
-    send_bytes(ExtInfo, D),
-    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
-
-
-%%% ######## {key_exchange_dh_gex_reply, client, init|renegotiate} ####
-
-handle_event(_, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) ->
-    {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params),
-    send_bytes(NewKeys, D),
-    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
-    send_bytes(ExtInfo, D),
-    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
-
-
-%%% ######## {new_keys, client|server} ####
-
-%% First key exchange round:
-handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,client,init}, D0) ->
-    {ok, Ssh1} = ssh_transport:handle_new_keys(Msg, D0#data.ssh_params),
-    %% {ok, ExtInfo, Ssh2} = ssh_transport:ext_info_message(Ssh1),
-    %% send_bytes(ExtInfo, D0),
-    {MsgReq, Ssh} = ssh_auth:service_request_msg(Ssh1),
-    D = send_msg(MsgReq, D0#data{ssh_params = Ssh}),
-    {next_state, {ext_info,client,init}, D};
-
-handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,server,init}, D) ->
-    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
-    %% {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
-    %% send_bytes(ExtInfo, D),
-    {next_state, {ext_info,server,init}, D#data{ssh_params=Ssh}};
-
-%% Subsequent key exchange rounds (renegotiation):
-handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,renegotiate}, D) ->
-    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
-    %% {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
-    %% send_bytes(ExtInfo, D),
-    {next_state, {ext_info,Role,renegotiate}, D#data{ssh_params=Ssh}};
-
-
-%%% ######## {ext_info, client|server, init|renegotiate} ####
-
-handle_event(_, #ssh_msg_ext_info{}=Msg, {ext_info,Role,init}, D0) ->
-    D = handle_ssh_msg_ext_info(Msg, D0),
-    {next_state, {service_request,Role}, D};
-
-handle_event(_, #ssh_msg_ext_info{}=Msg, {ext_info,Role,renegotiate}, D0) ->
-    D = handle_ssh_msg_ext_info(Msg, D0),
-    {next_state, {connected,Role}, D};
-
-handle_event(_, #ssh_msg_newkeys{}=Msg, {ext_info,_Role,renegotiate}, D) ->
-    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
-    {keep_state, D#data{ssh_params = Ssh}};
-    
 
-handle_event(internal, Msg, {ext_info,Role,init}, D) when is_tuple(Msg) ->
-    %% If something else arrives, goto next state and handle the event in that one
-    {next_state, {service_request,Role}, D, [postpone]};
 
-handle_event(internal, Msg, {ext_info,Role,_ReNegFlag}, D) when is_tuple(Msg) ->
-    %% If something else arrives, goto next state and handle the event in that one
-    {next_state, {connected,Role}, D, [postpone]};
-
-%%% ######## {service_request, client|server} ####
-
-handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {service_request,server}, D0) ->
+%%% ######## {service_request, client|server} #### StateName ==
+%% {userauth,server} guard added due to interoperability with clients
+%% sending extra ssh_msg_service_request (e.g. Paramiko for Python,
+%% see GH-6463)
+handle_event(internal, Msg = #ssh_msg_service_request{name=ServiceName}, StateName, D0)
+  when StateName == {service_request,server}; StateName == {userauth,server} ->
     case ServiceName of
 	"ssh-userauth" ->
 	    Ssh0 = #ssh{session_id=SessionId} = D0#data.ssh_params,
 	    {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
             D = send_msg(Reply, D0#data{ssh_params = Ssh}),
-	    {next_state, {userauth,server}, D};
+	    {next_state, {userauth,server}, D, {change_callback_module,ssh_fsm_userauth_server}};
 
 	_ ->
-            {Shutdown, D} =  
+            {Shutdown, D} =
                 ?send_disconnect(?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
                                  io_lib:format("Unknown service: ~p",[ServiceName]),
                                  StateName, D0),
             {stop, Shutdown, D}
     end;
 
-handle_event(_, #ssh_msg_service_accept{name = "ssh-userauth"}, {service_request,client},
+handle_event(internal, #ssh_msg_service_accept{name = "ssh-userauth"}, {service_request,client},
 	     #data{ssh_params = #ssh{service="ssh-userauth"} = Ssh0} = D0) ->
     {Msg, Ssh} = ssh_auth:init_userauth_request_msg(Ssh0),
     D = send_msg(Msg, D0#data{ssh_params = Ssh,
                               auth_user = Ssh#ssh.user
                              }),
-    {next_state, {userauth,client}, D};
-
-
-%%% ######## {userauth, client|server} ####
-
-%%---- userauth request to server
-handle_event(_, 
-	     Msg = #ssh_msg_userauth_request{service = ServiceName, method = Method},
-	     StateName = {userauth,server},
-	     D0 = #data{ssh_params=Ssh0}) ->
-
-    case {ServiceName, Ssh0#ssh.service, Method} of
-	{"ssh-connection", "ssh-connection", "none"} ->
-	    %% Probably the very first userauth_request but we deny unauthorized login
-	    {not_authorized, _, {Reply,Ssh}} =
-		ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0),
-            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
-	    {keep_state, D};
-	
-	{"ssh-connection", "ssh-connection", Method} ->
-	    %% Userauth request with a method like "password" or so
-	    case lists:member(Method, Ssh0#ssh.userauth_methods) of
-		true ->
-		    %% Yepp! we support this method
-		    case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of
-			{authorized, User, {Reply, Ssh1}} ->
-                            D = #data{ssh_params=Ssh} = 
-                                send_msg(Reply, D0#data{ssh_params = Ssh1}),
-			    D#data.starter ! ssh_connected,
-			    connected_fun(User, Method, D),
-			    {next_state, {connected,server},
-                             D#data{auth_user=User, 
-                                    %% Note: authenticated=true MUST NOT be sent
-                                    %% before send_msg!
-                                    ssh_params = Ssh#ssh{authenticated = true}}};
-			{not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" ->
-			    retry_fun(User, Reason, D0),
-                            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
-			    {next_state, {userauth_keyboard_interactive,server}, D};
-			{not_authorized, {User, Reason}, {Reply, Ssh}} ->
-			    retry_fun(User, Reason, D0),
-                            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
-			    {keep_state, D}
-		    end;
-		false ->
-		    %% No we do not support this method (=/= none)
-		    %% At least one non-erlang client does like this. Retry as the next event
-		    {keep_state_and_data,
-		     [{next_event, internal, Msg#ssh_msg_userauth_request{method="none"}}]
-		    }
-	    end;
-
-	%% {"ssh-connection", Expected, Method} when Expected =/= ServiceName -> Do what?
-	%% {ServiceName,      Expected, Method} when Expected =/= ServiceName -> Do what?
-
-	{ServiceName, _, _} when ServiceName =/= "ssh-connection" ->
-            {Shutdown, D} =  
-                ?send_disconnect(?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
-                                 io_lib:format("Unknown service: ~p",[ServiceName]),
-                                 StateName, D0),
-            {stop, Shutdown, D}
-    end;
-
-%%---- userauth success to client
-handle_event(_, #ssh_msg_ext_info{}=Msg, {userauth,client}, D0) ->
-    %% FIXME: need new state to receive this msg!
-    D = handle_ssh_msg_ext_info(Msg, D0),
-    {keep_state, D};
-
-handle_event(_, #ssh_msg_userauth_success{}, {userauth,client}, D=#data{ssh_params = Ssh}) ->
-    ssh_auth:ssh_msg_userauth_result(success),
-    D#data.starter ! ssh_connected,
-    {next_state, {connected,client}, D#data{ssh_params=Ssh#ssh{authenticated = true}}};
-
-
-%%---- userauth failure response to client
-handle_event(_, #ssh_msg_userauth_failure{}, {userauth,client}=StateName,
-	     #data{ssh_params = #ssh{userauth_methods = []}} = D0) ->
-    {Shutdown, D} =
-        ?send_disconnect(?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 
-                         io_lib:format("User auth failed for: ~p",[D0#data.auth_user]),
-                         StateName, D0),
-    {stop, Shutdown, D};
-handle_event(_, #ssh_msg_userauth_failure{authentications = Methods}, StateName={userauth,client},
-	     D0 = #data{ssh_params = Ssh0}) ->
-    %% The prefered authentication method failed try next method
-    Ssh1 = case Ssh0#ssh.userauth_methods of
-	       none ->
-		   %% Server tells us which authentication methods that are allowed
-		   Ssh0#ssh{userauth_methods = string:tokens(Methods, ",")};
-	       _ ->
-		   %% We already know...
-		   Ssh0
-	   end,
-    case ssh_auth:userauth_request_msg(Ssh1) of
-        {send_disconnect, Code, Ssh} ->
-            {Shutdown, D} =
-                ?send_disconnect(Code, 
-                                 io_lib:format("User auth failed for: ~p",[D0#data.auth_user]),
-                                 StateName, D0#data{ssh_params = Ssh}),
-	    {stop, Shutdown, D};
-	{"keyboard-interactive", {Msg, Ssh}} ->
-            D = send_msg(Msg, D0#data{ssh_params = Ssh}),
-	    {next_state, {userauth_keyboard_interactive,client}, D};
-	{_Method, {Msg, Ssh}} ->
-            D = send_msg(Msg, D0#data{ssh_params = Ssh}),
-	    {keep_state, D}
-    end;
-
-%%---- banner to client
-handle_event(_, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) ->
-    case D#data.ssh_params#ssh.userauth_quiet_mode of
-	false -> io:format("~s", [Msg]);
-	true -> ok
-    end,
-    keep_state_and_data;
+    {next_state, {userauth,client}, D, {change_callback_module,ssh_fsm_userauth_client}};
 
 
-%%% ######## {userauth_keyboard_interactive, client|server}
-
-handle_event(_, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client},
-	     #data{ssh_params = Ssh0} = D0) ->
-    case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of
-	{ok, {Reply, Ssh}} ->
-            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
-	    {next_state, {userauth_keyboard_interactive_info_response,client}, D};
-	not_ok ->
-	    {next_state, {userauth,client}, D0, [postpone]}
-    end;
-
-handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D0) ->
-    case ssh_auth:handle_userauth_info_response(Msg, D0#data.ssh_params) of
-	{authorized, User, {Reply, Ssh1}} ->
-            D = #data{ssh_params=Ssh} = 
-                send_msg(Reply, D0#data{ssh_params = Ssh1}),
-	    D#data.starter ! ssh_connected,
-	    connected_fun(User, "keyboard-interactive", D),
-	    {next_state, {connected,server}, D#data{auth_user = User,
-                                                    %% Note: authenticated=true MUST NOT be sent
-                                                    %% before send_msg!
-						    ssh_params = Ssh#ssh{authenticated = true}}};
-	{not_authorized, {User, Reason}, {Reply, Ssh}} ->
-	    retry_fun(User, Reason, D0),
-            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
-	    {next_state, {userauth,server}, D};
-
-	{authorized_but_one_more, _User,  {Reply, Ssh}} ->
-            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
-	    {next_state, {userauth_keyboard_interactive_extra,server}, D}
-    end;
-
-handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive_extra, server}, D0) ->
-    {authorized, User, {Reply, Ssh1}} =
-        ssh_auth:handle_userauth_info_response({extra,Msg}, D0#data.ssh_params),
-    D = #data{ssh_params=Ssh} = 
-        send_msg(Reply, D0#data{ssh_params = Ssh1}),
-    D#data.starter ! ssh_connected,
-    connected_fun(User, "keyboard-interactive", D),
-    {next_state, {connected,server}, D#data{auth_user = User,
-                                            %% Note: authenticated=true MUST NOT be sent
-                                            %% before send_msg!
-					    ssh_params = Ssh#ssh{authenticated = true}}};
-
-handle_event(_, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client},
-	     #data{ssh_params = Ssh0} = D0) ->
-    Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference,
-			       Method =/= "keyboard-interactive"],
-    D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}},
-    {next_state, {userauth,client}, D, [postpone]};
-
-handle_event(_, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client},
-	     #data{ssh_params = Ssh0} = D0) ->
-    Opts = Ssh0#ssh.opts,
-    D = case ?GET_OPT(password, Opts) of
-	    undefined ->
-		D0;
-	    _ ->
-		D0#data{ssh_params =
-			    Ssh0#ssh{opts = ?PUT_OPT({password,not_ok}, Opts)}} % FIXME:intermodule dependency
-	end,
-    {next_state, {userauth,client}, D, [postpone]};
-
-handle_event(_, #ssh_msg_ext_info{}=Msg, {userauth_keyboard_interactive_info_response, client}, D0) ->
-    %% FIXME: need new state to receive this msg!
-    D = handle_ssh_msg_ext_info(Msg, D0),
-    {keep_state, D};
-
-handle_event(_, #ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) ->
-    {next_state, {userauth,client}, D, [postpone]};
-
-handle_event(_, #ssh_msg_userauth_info_request{}, {userauth_keyboard_interactive_info_response, client}, D) ->
-    {next_state, {userauth_keyboard_interactive,client}, D, [postpone]};
-
-
-%%% ######## {connected, client|server} ####
-
 %% Skip ext_info messages in connected state (for example from OpenSSH >= 7.7)
-handle_event(_, #ssh_msg_ext_info{}, {connected,_Role}, D) ->
+handle_event(internal, #ssh_msg_ext_info{}, {connected,_Role}, D) ->
     {keep_state, D};
 
-handle_event(_, {#ssh_msg_kexinit{},_}, {connected,Role}, D0) ->
+handle_event(internal, {#ssh_msg_kexinit{},_}, {connected,Role}, D0) ->
     {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(D0#data.ssh_params),
     D = D0#data{ssh_params = Ssh,
 		key_exchange_init_msg = KeyInitMsg},
     send_bytes(SshPacket, D),
-    {next_state, {kexinit,Role,renegotiate}, D, [postpone]};
+    {next_state, {kexinit,Role,renegotiate}, D, [postpone, {change_callback_module,ssh_fsm_kexinit}]};
 
-handle_event(_, #ssh_msg_disconnect{description=Desc} = Msg, StateName, D0) ->
+handle_event(internal, #ssh_msg_disconnect{description=Desc} = Msg, StateName, D0) ->
     {disconnect, _, RepliesCon} =
-	ssh_connection:handle_msg(Msg, D0#data.connection_state, role(StateName), D0#data.ssh_params),
+	ssh_connection:handle_msg(Msg, D0#data.connection_state, ?role(StateName), D0#data.ssh_params),
     {Actions,D} = send_replies(RepliesCon, D0),
     disconnect_fun("Received disconnect: "++Desc, D),
     {stop_and_reply, {shutdown,Desc}, Actions, D};
 
-handle_event(_, #ssh_msg_ignore{}, _, _) ->
+handle_event(internal, #ssh_msg_ignore{}, _StateName, _) ->
     keep_state_and_data;
 
-handle_event(_, #ssh_msg_unimplemented{}, _, _) ->
+handle_event(internal, #ssh_msg_unimplemented{}, _StateName, _) ->
     keep_state_and_data;
 
-handle_event(_, #ssh_msg_debug{} = Msg, _, D) ->
+%% Quick fix of failing test case (ssh_options_SUITE:ssh_msg_debug_fun_option_{client|server}/1)
+handle_event(cast, #ssh_msg_debug{} = Msg, State, D) ->
+    handle_event(internal, Msg, State, D);
+
+handle_event(internal, #ssh_msg_debug{} = Msg, _StateName, D) ->
     debug_fun(Msg, D),
     keep_state_and_data;
 
-handle_event(internal, {conn_msg,Msg}, StateName, #data{starter = User,
-                                                        connection_state = Connection0,
+handle_event(internal, {conn_msg,Msg}, StateName, #data{connection_state = Connection0,
                                                         event_queue = Qev0} = D0) ->
-    Role = role(StateName),
+    Role = ?role(StateName),
     Rengotation = renegotiation(StateName),
     try ssh_connection:handle_msg(Msg, Connection0, Role, D0#data.ssh_params) of
 	{disconnect, Reason0, RepliesConn} ->
@@ -1140,7 +756,7 @@ handle_event(internal, {conn_msg,Msg}, S
             case {Reason0,Role} of
                 {{_, Reason}, client} when ((StateName =/= {connected,client})
                                             and (not Rengotation)) ->
-		   User ! {self(), not_connected, Reason};
+                    handshake({not_connected,Reason}, D);
                 _ ->
                     ok
             end,
@@ -1178,7 +794,7 @@ handle_event(internal, {conn_msg,Msg}, S
 handle_event(enter, OldState, {connected,_}=NewState, D) ->
     %% Entering the state where re-negotiation is possible
     init_renegotiate_timers(OldState, NewState, D);
-    
+
 handle_event(enter, OldState, {ext_info,_,renegotiate}=NewState, D) ->
     %% Could be hanging in exit_info state if nothing else arrives
     init_renegotiate_timers(OldState, NewState, D);
@@ -1247,7 +863,7 @@ handle_event(cast, {adjust_window,Channe
 handle_event(cast, {reply_request,Resp,ChannelId}, StateName, D) when ?CONNECTED(StateName) ->
     case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
         #channel{remote_id = RemoteId} when Resp== success ; Resp==failure ->
-            Msg = 
+            Msg =
                 case Resp of
                     success -> ssh_connection:channel_success_msg(RemoteId);
                     failure -> ssh_connection:channel_failure_msg(RemoteId)
@@ -1257,7 +873,9 @@ handle_event(cast, {reply_request,Resp,C
 
         #channel{} ->
             Details = io_lib:format("Unhandled reply in state ~p:~n~p", [StateName,Resp]),
-            ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, Details, StateName, D);
+            {_Shutdown, D1} =
+                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, Details, StateName, D),
+            {keep_state, D1};
 
 	undefined ->
 	    keep_state_and_data
@@ -1285,7 +903,7 @@ handle_event({call,From}, get_print_info
 	     inet:peername(D#data.socket)
 	    }
 	of
-	    {{ok,Local}, {ok,Remote}} -> 
+	    {{ok,Local}, {ok,Remote}} ->
 		{{Local,Remote},io_lib:format("statename=~p",[StateName])};
 	    _ ->
 		{{"-",0},"-"}
@@ -1315,7 +933,7 @@ handle_event({call,From}, {info, all}, _
 				     end,
 				     [], cache(D)),
     {keep_state_and_data, [{reply, From, {ok,Result}}]};
-    
+
 handle_event({call,From}, {info, ChannelPid}, _, D) ->
     Result = ssh_client_channel:cache_foldl(
 	       fun(Channel, Acc) when Channel#channel.user == ChannelPid ->
@@ -1346,24 +964,24 @@ handle_event({call,From}, stop, _StateNa
 handle_event({call,_}, _, StateName, _) when not ?CONNECTED(StateName) ->
     {keep_state_and_data, [postpone]};
 
-handle_event({call,From}, {request, ChannelPid, ChannelId, Type, Data, Timeout}, StateName, D0) 
+handle_event({call,From}, {request, ChannelPid, ChannelId, Type, Data, Timeout}, StateName, D0)
   when ?CONNECTED(StateName) ->
     case handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0) of
         {error,Error} ->
             {keep_state, D0, {reply,From,{error,Error}}};
         D ->
-            %% Note reply to channel will happen later when reply is recived from peer on the socket
+            %% Note reply to channel will happen later when reply is received from peer on the socket
             start_channel_request_timer(ChannelId, From, Timeout),
             {keep_state, D, cond_set_idle_timer(D)}
     end;
 
-handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, StateName, D0) 
+handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, StateName, D0)
   when ?CONNECTED(StateName) ->
     case handle_request(ChannelId, Type, Data, true, From, D0) of
         {error,Error} ->
             {keep_state, D0, {reply,From,{error,Error}}};
         D ->
-            %% Note reply to channel will happen later when reply is recived from peer on the socket
+            %% Note reply to channel will happen later when reply is received from peer on the socket
             start_channel_request_timer(ChannelId, From, Timeout),
             {keep_state, D, cond_set_idle_timer(D)}
     end;
@@ -1396,14 +1014,14 @@ handle_event({call,From}, {global_reques
     start_channel_request_timer(Id, From, Timeout),
     {keep_state, D, cond_set_idle_timer(D)};
 
-handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, StateName, D0) 
+handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, StateName, D0)
   when ?CONNECTED(StateName) ->
     {Repls,D} = send_replies(ssh_connection:channel_data(ChannelId, Type, Data, D0#data.connection_state, From),
                              D0),
     start_channel_request_timer(ChannelId, From, Timeout), % FIXME: No message exchange so why?
     {keep_state, D, Repls};
 
-handle_event({call,From}, {eof, ChannelId}, StateName, D0) 
+handle_event({call,From}, {eof, ChannelId}, StateName, D0)
   when ?CONNECTED(StateName) ->
     case ssh_client_channel:cache_lookup(cache(D0), ChannelId) of
 	#channel{remote_id = Id, sent_close = false} ->
@@ -1415,35 +1033,40 @@ handle_event({call,From}, {eof, ChannelI
 
 handle_event({call,From}, get_misc, StateName,
              #data{connection_state = #connection{options = Opts}} = D) when ?CONNECTED(StateName) ->
-    Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
-    SubSysSup = proplists:get_value(subsystem_sup,  Sups),
-    Reply = {ok, {SubSysSup, role(StateName), Opts}},
+    ConnectionSup = ?GET_INTERNAL_OPT(connection_sup, Opts),
+    Reply = {ok, {ConnectionSup, ?role(StateName), Opts}},
     {keep_state, D, [{reply,From,Reply}]};
 
 handle_event({call,From},
 	     {open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data, Timeout},
 	     StateName,
-	     D0) when ?CONNECTED(StateName) ->
+	     D0 = #data{connection_state = C}) when ?CONNECTED(StateName) ->
     erlang:monitor(process, ChannelPid),
     {ChannelId, D1} = new_channel_id(D0),
-    D2 = send_msg(ssh_connection:channel_open_msg(Type, ChannelId,
-						  InitialWindowSize,
-						  MaxPacketSize, Data),
+    WinSz = case InitialWindowSize of
+                undefined -> C#connection.suggest_window_size;
+                _ -> InitialWindowSize
+            end,
+    PktSz = case MaxPacketSize of
+                undefined -> C#connection.suggest_packet_size;
+                _ -> MaxPacketSize
+            end,
+    D2 = send_msg(ssh_connection:channel_open_msg(Type, ChannelId, WinSz, PktSz, Data),
 		  D1),
-    ssh_client_channel:cache_update(cache(D2), 
+    ssh_client_channel:cache_update(cache(D2),
 			     #channel{type = Type,
 				      sys = "none",
 				      user = ChannelPid,
 				      local_id = ChannelId,
-				      recv_window_size = InitialWindowSize,
-				      recv_packet_size = MaxPacketSize,
+				      recv_window_size = WinSz,
+				      recv_packet_size = PktSz,
 				      send_buf = queue:new()
 				     }),
     D = add_request(true, ChannelId, From, D2),
     start_channel_request_timer(ChannelId, From, Timeout),
     {keep_state, D, cond_set_idle_timer(D)};
 
-handle_event({call,From}, {send_window, ChannelId}, StateName, D) 
+handle_event({call,From}, {send_window, ChannelId}, StateName, D)
   when ?CONNECTED(StateName) ->
     Reply = case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
 		#channel{send_window_size = WinSize,
@@ -1454,7 +1077,7 @@ handle_event({call,From}, {send_window,
 	    end,
     {keep_state_and_data, [{reply,From,Reply}]};
 
-handle_event({call,From}, {recv_window, ChannelId}, StateName, D) 
+handle_event({call,From}, {recv_window, ChannelId}, StateName, D)
   when ?CONNECTED(StateName) ->
     Reply = case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
 		#channel{recv_window_size = WinSize,
@@ -1465,14 +1088,24 @@ handle_event({call,From}, {recv_window,
 	    end,
     {keep_state_and_data, [{reply,From,Reply}]};
 
-handle_event({call,From}, {close, ChannelId}, StateName, D0) 
+handle_event({call,From}, {close, ChannelId}, StateName, D0)
   when ?CONNECTED(StateName) ->
+    %% Send 'channel-close' only if it has not been sent yet
+    %% e.g. when 'exit-signal' was received from the peer
+    %% and(!) we update the cache so that we remember what we've done
     case ssh_client_channel:cache_lookup(cache(D0), ChannelId) of
-	#channel{remote_id = Id} = Channel ->
+	#channel{remote_id = Id, sent_close = false} = Channel ->
 	    D1 = send_msg(ssh_connection:channel_close_msg(Id), D0),
-	    ssh_client_channel:cache_update(cache(D1), Channel#channel{sent_close = true}),
-	    {keep_state, D1, [cond_set_idle_timer(D1), {reply,From,ok}]};
-	undefined ->
+	    ssh_client_channel:cache_update(cache(D1),
+                                            Channel#channel{sent_close = true}),
+	    {keep_state, D1, [cond_set_idle_timer(D1),
+                              channel_close_timer(D1, Id),
+                              {reply,From,ok}]};
+	_ ->
+            %% Here we match a channel which has already sent 'channel-close'
+            %% AND possible cases of 'broken cache' i.e. when a channel
+            %% disappeared from the cache, but has not been properly shut down
+            %% The latter would be a bug, but hard to chase
 	    {keep_state_and_data, [{reply,From,ok}]}
     end;
 
@@ -1492,15 +1125,17 @@ handle_event({call,From}, {retrieve,Key}
 handle_event(info, {Proto, Sock, Info}, {hello,_}, #data{socket = Sock,
 							 transport_protocol = Proto}) ->
     case Info of
-	"SSH-" ++ _ ->  
+	"SSH-" ++ _ ->
 	    {keep_state_and_data, [{next_event, internal, {version_exchange,Info}}]};
 	_ ->
 	    {keep_state_and_data, [{next_event, internal, {info_line,Info}}]}
     end;
 
 
-handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock,
-								 transport_protocol = Proto}) ->
+handle_event(info, {Proto, Sock, NewData}, StateName,
+             D0 = #data{socket = Sock,
+                        transport_protocol = Proto,
+                        ssh_params = SshParams}) ->
     try ssh_transport:handle_packet_part(
 	  D0#data.decrypted_data_buffer,
 	  <<(D0#data.encrypted_data_buffer)/binary, NewData/binary>>,
@@ -1510,11 +1145,14 @@ handle_event(info, {Proto, Sock, NewData
     of
 	{packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} ->
 	    D1 = D0#data{ssh_params =
-			    Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)},
-			decrypted_data_buffer = <<>>,
-                        undecrypted_packet_length = undefined,
-                        aead_data = <<>>,
-			encrypted_data_buffer = EncryptedDataRest},
+                             Ssh1#ssh{recv_sequence =
+                                          ssh_transport:next_seqnum(StateName,
+                                                                    Ssh1#ssh.recv_sequence,
+                                                                    SshParams)},
+                         decrypted_data_buffer = <<>>,
+                         undecrypted_packet_length = undefined,
+                         aead_data = <<>>,
+                         encrypted_data_buffer = EncryptedDataRest},
 	    try
 		ssh_message:decode(set_kex_overload_prefix(DecryptedBytes,D1))
 	    of
@@ -1526,7 +1164,10 @@ handle_event(info, {Proto, Sock, NewData
                 #ssh_msg_global_request{}            = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
                 #ssh_msg_request_success{}           = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
                 #ssh_msg_request_failure{}           = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
-                #ssh_msg_channel_open{}              = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
+                #ssh_msg_channel_open{}              = Msg -> {keep_state, D1,
+                                                               [{{timeout, max_initial_idle_time}, cancel} |
+                                                                ?CONNECTION_MSG(Msg)
+                                                               ]};
                 #ssh_msg_channel_open_confirmation{} = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
                 #ssh_msg_channel_open_failure{}      = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
                 #ssh_msg_channel_window_adjust{}     = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
@@ -1543,11 +1184,21 @@ handle_event(info, {Proto, Sock, NewData
                                       {next_event, internal, Msg}
 				    ]}
 	    catch
-		C:E:ST  ->
-                    {Shutdown, D} =  
+		Class:Reason0:Stacktrace  ->
+                    Reason = ssh_lib:trim_reason(Reason0),
+                    MsgFun =
+                        fun(debug) ->
+                                io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~p",
+                                              [Class,Reason,Stacktrace],
+                                              [{chars_limit, ssh_lib:max_log_len(SshParams)}]);
+                           (_) ->
+                                io_lib:format("Bad packet: Decrypted, but can't decode ~p:~p",
+                                              [Class, Reason],
+                                              [{chars_limit, ssh_lib:max_log_len(SshParams)}])
+                        end,
+                    {Shutdown, D} =
                         ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
-                                         io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~p",
-                                                       [C,E,ST]),
+                                         ?SELECT_MSG(MsgFun),
                                          StateName, D1),
                     {stop, Shutdown, D}
 	    end;
@@ -1563,33 +1214,43 @@ handle_event(info, {Proto, Sock, NewData
 				 ssh_params = Ssh1}};
 
 	{bad_mac, Ssh1} ->
-            {Shutdown, D} =  
+            {Shutdown, D} =
                 ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
                                  "Bad packet: bad mac",
                                  StateName, D0#data{ssh_params=Ssh1}),
             {stop, Shutdown, D};
 
 	{error, {exceeds_max_size,PacketLen}} ->
-            {Shutdown, D} =  
+            {Shutdown, D} =
                 ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
                                  io_lib:format("Bad packet: Size (~p bytes) exceeds max size",
                                                [PacketLen]),
                                  StateName, D0),
             {stop, Shutdown, D}
     catch
-	C:E:ST ->
-            {Shutdown, D} =  
-                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
-                                 io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~p",[C,E,ST]),
+	Class:Reason0:Stacktrace ->
+            MsgFun =
+                fun(debug) ->
+                        io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~p",
+                                      [Class,Reason0,Stacktrace],
+                                      [{chars_limit, ssh_lib:max_log_len(SshParams)}]);
+                   (_) ->
+                        Reason = ssh_lib:trim_reason(Reason0),
+                        io_lib:format("Bad packet: Couldn't decrypt~n~p:~p",
+                                      [Class,Reason],
+                                      [{chars_limit, ssh_lib:max_log_len(SshParams)}])
+                end,
+            {Shutdown, D} =
+                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, ?SELECT_MSG(MsgFun),
                                  StateName, D0),
             {stop, Shutdown, D}
     end;
 
 
-%%%==== 
-handle_event(internal, prepare_next_packet, _, D) ->
+%%%====
+handle_event(internal, prepare_next_packet, _StateName, D) ->
     Enough =  erlang:max(8, D#data.ssh_params#ssh.decrypt_block_size),
-    case size(D#data.encrypted_data_buffer) of
+    case byte_size(D#data.encrypted_data_buffer) of
 	Sz when Sz >= Enough ->
 	    self() ! {D#data.transport_protocol, D#data.socket, <<>>};
 	_ ->
@@ -1622,27 +1283,58 @@ handle_event(info, {timeout, {_, From} =
 %%% Handle that ssh channels user process goes down
 handle_event(info, {'DOWN', _Ref, process, ChannelPid, _Reason}, _, D) ->
     Cache = cache(D),
-    ssh_client_channel:cache_foldl(
-      fun(#channel{user=U,
-                   local_id=Id}, Acc) when U == ChannelPid ->
-              ssh_client_channel:cache_delete(Cache, Id),
-              Acc;
-         (_,Acc) ->
-              Acc
-      end, [], Cache),
-    {keep_state, D, cond_set_idle_timer(D)};
+    %% Here we first collect the list of channel id's  handled by the process
+    %% Do NOT remove them from the cache - they are not closed yet!
+    Channels = ssh_client_channel:cache_foldl(
+                 fun(#channel{user=U} = Channel, Acc) when U == ChannelPid ->
+                         [Channel | Acc];
+                    (_,Acc) ->
+                         Acc
+                 end, [], Cache),
+    %% Then for each channel where 'channel-close' has not been sent yet
+    %% we send 'channel-close' and(!) update the cache so that we remember
+    %% what we've done.
+    %% Also set user as 'undefined' as there is no such process anyway
+    {D2, NewTimers} = lists:foldl(
+                        fun(#channel{remote_id = Id, sent_close = false} = Channel,
+                            {D0, Timers}) when Id /= undefined ->
+                                D1 = send_msg(ssh_connection:channel_close_msg(Id), D0),
+                                ssh_client_channel:cache_update(cache(D1),
+                                                                Channel#channel{sent_close = true,
+                                                                                user = undefined}),
+                                ChannelTimer = channel_close_timer(D1, Id),
+                                {D1, [ChannelTimer | Timers]};
+                           (Channel, {D0, _} = Acc) ->
+                                ssh_client_channel:cache_update(cache(D0),
+                                                                Channel#channel{user = undefined}),
+                                Acc
+                        end, {D, []}, Channels),
+    {keep_state, D2, [cond_set_idle_timer(D2) | NewTimers]};
 
 handle_event({timeout,idle_time}, _Data,  _StateName, D) ->
     case ssh_client_channel:cache_info(num_entries, cache(D)) of
-        0 -> 
+        0 ->
             {stop, {shutdown, "Timeout"}};
         _ ->
             keep_state_and_data
     end;
 
+handle_event({timeout,max_initial_idle_time}, _Data,  _StateName, _D) ->
+    {stop, {shutdown, "Timeout"}};
+
+handle_event({timeout, {channel_close, ChannelId}}, _Data, _StateName, D) ->
+    Cache = cache(D),
+    case ssh_client_channel:cache_lookup(Cache, ChannelId) of
+        #channel{sent_close = true} ->
+            ssh_client_channel:cache_delete(Cache, ChannelId),
+            {keep_state, D, cond_set_idle_timer(D)};
+        _ ->
+            keep_state_and_data
+    end;
+
 %%% So that terminate will be run when supervisor is shutdown
-handle_event(info, {'EXIT', _Sup, Reason}, StateName, _) ->
-    Role = role(StateName),
+handle_event(info, {'EXIT', _Sup, Reason}, StateName, _D) ->
+    Role = ?role(StateName),
     if
 	Role == client ->
 	    %% OTP-8111 tells this function clause fixes a problem in
@@ -1650,7 +1342,7 @@ handle_event(info, {'EXIT', _Sup, Reason
 	    {stop, {shutdown, Reason}};
 
 	Reason == normal ->
-	    %% An exit normal should not cause a server to crash. This has happend...
+	    %% An exit normal should not cause a server to crash. This has happened...
 	    keep_state_and_data;
 
 	true ->
@@ -1663,9 +1355,9 @@ handle_event(info, check_cache, _, D) ->
 handle_event(info, {fwd_connect_received, Sock, ChId, ChanCB}, StateName, #data{connection_state = Connection}) ->
     #connection{options = Options,
                 channel_cache = Cache,
-                sub_system_supervisor = SubSysSup} = Connection,
+                connection_supervisor = ConnectionSup} = Connection,
     Channel = ssh_client_channel:cache_lookup(Cache, ChId),
-    {ok,Pid} = ssh_subsystem_sup:start_channel(role(StateName), SubSysSup, self(), ChanCB, ChId, [Sock], undefined, Options),
+    {ok,Pid} = ssh_connection_sup:start_channel(?role(StateName), ConnectionSup, self(), ChanCB, ChId, [Sock], undefined, Options),
     ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}),
     gen_tcp:controlling_process(Sock, Pid),
     inet:setopts(Sock, [{active,once}]),
@@ -1674,8 +1366,8 @@ handle_event(info, {fwd_connect_received
 handle_event({call,From},
              {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, _Timeout},
              _StateName,
-             #data{connection_state = #connection{sub_system_supervisor=SubSysSup}}) ->
-    case ssh_tcpip_forward_acceptor:supervised_start(ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
+             #data{connection_state = #connection{connection_supervisor=ConnectionSup}}) ->
+    case ssh_tcpip_forward_acceptor:supervised_start(ssh_connection_sup:tcpip_fwd_supervisor(ConnectionSup),
                                                      {ListenHost, ListenPort},
                                                      {ConnectToHost, ConnectToPort},
                                                      "direct-tcpip", ssh_tcpip_forward_client,
@@ -1698,7 +1390,7 @@ handle_event(info, UnexpectedMessage, St
 		      "Local Address: ~p\n",
                       [UnexpectedMessage,
                        StateName,
-                       Ssh#ssh.role, 
+                       Ssh#ssh.role,
                        Ssh#ssh.peer,
                        ?GET_INTERNAL_OPT(address, Ssh#ssh.opts, undefined)])),
 	    error_logger:info_report(Msg),
@@ -1727,7 +1419,7 @@ handle_event(info, UnexpectedMessage, St
     end;
 
 handle_event(internal, {send_disconnect,Code,DetailedText,Module,Line}, StateName, D0) ->
-    {Shutdown, D} =  
+    {Shutdown, D} =
         send_disconnect(Code, DetailedText, Module, Line, StateName, D0),
     {stop, Shutdown, D};
 
@@ -1746,9 +1438,9 @@ handle_event(Type, Ev, StateName, D0) ->
 	    "ssh_msg_" ++_ when Type==internal ->
                 lists:flatten(io_lib:format("Message ~p in wrong state (~p)", [element(1,Ev), StateName]));
 	    _ ->
-		io_lib:format("Unhandled event in state ~p:~n~p", [StateName,Ev])
+		io_lib:format("Unhandled event in state ~p and type ~p:~n~p", [StateName,Type,Ev])
 	end,
-    {Shutdown, D} =  
+    {Shutdown, D} =
         ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, Details, StateName, D0),
     {stop, Shutdown, D}.
 
@@ -1758,27 +1450,18 @@ handle_event(Type, Ev, StateName, D0) ->
 		state_name(),
 		#data{}
 	       ) -> term().
-			
+
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+terminate(_, {wait_for_socket, _}, _) ->
+    %% No need to to anything - maybe we have not yet gotten
+    %% control over the socket
+    ok;
 
 terminate(normal, _StateName, D) ->
-    stop_subsystem(D),
-    close_transport(D);
-
-terminate({shutdown,"Connection closed"}, _StateName, D) ->
-    %% Normal: terminated by a sent by peer
-    stop_subsystem(D),
-    close_transport(D);
-
-terminate({shutdown,{init,Reason}}, StateName, D) ->
-    %% Error in initiation. "This error should not occur".
-    log(error, D, "Shutdown in init (StateName=~p): ~p~n", [StateName,Reason]),
-    stop_subsystem(D),
     close_transport(D);
 
 terminate({shutdown,_R}, _StateName, D) ->
     %% Internal termination, usually already reported via ?send_disconnect resulting in a log entry
-    stop_subsystem(D),
     close_transport(D);
 
 terminate(shutdown, _StateName, D0) ->
@@ -1789,11 +1472,6 @@ terminate(shutdown, _StateName, D0) ->
                  D0),
     close_transport(D);
 
-terminate(killed, _StateName, D) ->
-    %% Got a killed signal
-    stop_subsystem(D),
-    close_transport(D);
-
 terminate(Reason, StateName, D0) ->
     %% Others, e.g  undef, {badmatch,_}, ...
     log(error, D0, Reason),
@@ -1801,16 +1479,21 @@ terminate(Reason, StateName, D0) ->
                                             "Internal error",
                                             io_lib:format("Reason: ~p",[Reason]),
                                             StateName, D0),
-    stop_subsystem(D),
     close_transport(D).
 
 %%--------------------------------------------------------------------
 
 %% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
-format_status(normal, [_, _StateName, D]) ->
+format_status(A, B) ->
+    try format_status0(A, B)
+    catch
+        _:_ -> "????"
+    end.
+
+format_status0(normal, [_PDict, _StateName, D]) ->
     [{data, [{"State", D}]}];
-format_status(terminate, [_, _StateName, D]) ->
+format_status0(terminate, [_, _StateName, D]) ->
     [{data, [{"State", clean(D)}]}].
 
 
@@ -1852,7 +1535,6 @@ clean(T) when is_tuple(T) ->
 clean(X) ->
     ssh_options:no_sensitive(filter, X).
 
-
 fmt_stat_rec(FieldNames, Rec, Exclude) ->
     Values = tl(tuple_to_list(Rec)),
     list_to_tuple(
@@ -1883,70 +1565,10 @@ code_change(_OldVsn, StateName, State, _
 %%====================================================================
 
 %%--------------------------------------------------------------------
-%% Starting
-
-start_the_connection_child(UserPid, Role, Socket, Options0) ->
-    Sups = ?GET_INTERNAL_OPT(supervisors, Options0),
-    ConnectionSup = proplists:get_value(connection_sup, Sups),
-    Options = ?PUT_INTERNAL_OPT({user_pid,UserPid}, Options0),
-    InitArgs = [Role, Socket, Options],
-    {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, InitArgs),
-    ok = socket_control(Socket, Pid, Options), % transfer the Socket ownership in a controlled way.
-    Pid.
-
-%%--------------------------------------------------------------------
-%% Stopping
-
-stop_subsystem(#data{ssh_params = 
-                         #ssh{role = Role},
-                     connection_state =
-                         #connection{system_supervisor = SysSup,
-                                     sub_system_supervisor = SubSysSup,
-                                     options = Opts}
-                    }) when is_pid(SysSup) andalso is_pid(SubSysSup)  ->
-    C = self(),
-    spawn(fun() ->
-                  wait_until_dead(C, 10000),
-                  case {Role, ?GET_INTERNAL_OPT(connected_socket,Opts,non_socket_started)} of
-                      {server, non_socket_started} ->
-                          ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
-                      {client, non_socket_started} ->
-                          ssh_system_sup:stop_system(Role, SysSup);
-                      {server, _Socket} ->
-                          ssh_system_sup:stop_system(Role, SysSup);
-                      {client, _Socket} ->
-                          ssh_system_sup:stop_subsystem(SysSup, SubSysSup),
-                          wait_until_dead(SubSysSup, 1000),
-                          sshc_sup:stop_system(SysSup)
-                  end
-          end);
-stop_subsystem(_) ->
-    ok.
-
-
-wait_until_dead(Pid, Timeout) ->
-    Mref = erlang:monitor(process, Pid),
-    receive
-        {'DOWN', Mref, process, Pid, _Info} -> ok
-    after
-        Timeout -> ok
-    end.
-
-
 close_transport(#data{transport_cb = Transport,
                       socket = Socket}) ->
-    try
-        Transport:close(Socket)
-    of
-        _ -> ok
-    catch
-        _:_ -> ok
-    end.
-
-%%--------------------------------------------------------------------
-%% "Invert" the Role
-peer_role(client) -> server;
-peer_role(server) -> client.
+    catch Transport:close(Socket),
+    ok.
 
 %%--------------------------------------------------------------------
 available_hkey_algorithms(client, Options) ->
@@ -2051,7 +1673,7 @@ kex(#ssh{algorithms=#alg{kex=Kex}}) -> K
 kex(_) -> undefined.
 
 cache(#data{connection_state=C}) -> C#connection.channel_cache.
-    
+
 
 %%%----------------------------------------------------------------
 handle_ssh_msg_ext_info(#ssh_msg_ext_info{}, D=#data{ssh_params = #ssh{recv_ext_info=false}} ) ->
@@ -2065,7 +1687,7 @@ handle_ssh_msg_ext_info(#ssh_msg_ext_inf
 ext_info({"server-sig-algs",SigAlgsStr},
          D0 = #data{ssh_params=#ssh{role=client,
                                     userauth_pubkeys=ClientSigAlgs}=Ssh0}) ->
-    %% ClientSigAlgs are the pub_key algortithms that:
+    %% ClientSigAlgs are the pub_key algorithms that:
     %%  1) is usable, that is, the user has such a public key and
     %%  2) is either the default list or set by the caller
     %%     with the client option 'pref_public_key_algs'
@@ -2140,7 +1762,7 @@ handle_request(ChannelId, Type, Data, Wa
 
 	_ when WantReply==true ->
             {error,closed};
-        
+
         _ ->
             D
     end.
@@ -2176,7 +1798,7 @@ start_rekeying(Role, D0) ->
     send_bytes(SshPacket, D0),
     D = D0#data{ssh_params = Ssh,
                 key_exchange_init_msg = KeyInitMsg},
-    {next_state, {kexinit,Role,renegotiate}, D}.
+    {next_state, {kexinit,Role,renegotiate}, D, {change_callback_module,ssh_fsm_kexinit}}.
 
 
 init_renegotiate_timers(_OldState, NewState, D) ->
@@ -2291,7 +1913,7 @@ conn_info(channels, D) -> try conn_info_
                           end;
 %% dbg options ( = not documented):
 conn_info(socket, D) ->   D#data.socket;
-conn_info(chan_ids, D) -> 
+conn_info(chan_ids, D) ->
     ssh_client_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) ->
 				    [Id | Acc]
 			    end, [], cache(D)).
@@ -2357,15 +1979,18 @@ do_log(F, Reason0, #data{ssh_params=S})
                                Role==client ->
             {PeerRole,PeerVersion} =
                 case Role of
-                    server -> {"Client", S#ssh.c_version};
-                    client -> {"Server", S#ssh.s_version}
+                    server -> {"Peer client", S#ssh.c_version};
+                    client -> {"Peer server", S#ssh.s_version}
                 end,
-            error_logger:F("Erlang SSH ~p ~s ~s.~n"
-                           "~s: ~p~n"
+            error_logger:F("Erlang SSH ~p version: ~s ~s.~n"
+                           "Address: ~s~n"
+                           "~s version: ~p~n"
+                           "Peer address: ~s~n"
                            "~s~n",
-                           [Role,
-                            ssh_log_version(), crypto_log_info(), 
+                           [Role, ssh_log_version(), crypto_log_info(),
+                            ssh_lib:format_address_port(S#ssh.local),
                             PeerRole, PeerVersion,
+                            ssh_lib:format_address_port(element(2,S#ssh.peer)),
                             Reason]);
         _ ->
             error_logger:F("Erlang SSH ~s ~s.~n"
@@ -2376,7 +2001,7 @@ do_log(F, Reason0, #data{ssh_params=S})
 
 assure_string(S) ->
     try io_lib:format("~s",[S])
-    of _ -> S
+    of Formatted -> Formatted
     catch
         _:_ -> io_lib:format("~p",[S])
     end.
@@ -2393,12 +2018,12 @@ limit_size(S, Len, MaxLen) when Len =< (
     S;
 limit_size(S, Len, MaxLen) when Len > MaxLen ->
     %% Cut
-    io_lib:format("~s ... (~w bytes skipped)", 
+    io_lib:format("~s ... (~w bytes skipped)",
                   [string:substr(lists:flatten(S), 1, MaxLen),
                    Len-MaxLen]).
 
 crypto_log_info() ->
-    try 
+    try
         [{_,_,CI}] = crypto:info_lib(),
         case crypto:info_fips() of
             enabled ->
@@ -2456,8 +2081,6 @@ get_repl(X, Acc) ->
     exit({get_repl,X,Acc}).
 
 %%%----------------------------------------------------------------
--define(CALL_FUN(Key,D), catch (?GET_OPT(Key, (D#data.ssh_params)#ssh.opts)) ).
-
 %%disconnect_fun({disconnect,Msg}, D) -> ?CALL_FUN(disconnectfun,D)(Msg);
 disconnect_fun(Reason, D)           -> ?CALL_FUN(disconnectfun,D)(Reason).
 
@@ -2471,36 +2094,6 @@ debug_fun(#ssh_msg_debug{always_display
     ?CALL_FUN(ssh_msg_debug_fun,D)(self(), Display, DbgMsg, Lang).
 
 
-connected_fun(User, Method, #data{ssh_params = #ssh{peer = {_,Peer}}} = D) ->
-    ?CALL_FUN(connectfun,D)(User, Peer, Method).
-
-
-retry_fun(_, undefined, _) ->
-    ok;
-retry_fun(User, Reason, #data{ssh_params = #ssh{opts = Opts,
-						peer = {_,Peer}
-					       }}) ->
-    {Tag,Info} =
-	case Reason of
-	    {error, Error} ->
-		{failfun, Error};
-	    _ ->
-		{infofun, Reason}
-	end,
-    Fun = ?GET_OPT(Tag, Opts),
-    try erlang:fun_info(Fun, arity)
-    of
-	{arity, 2} -> %% Backwards compatible
-	    catch Fun(User, Info);
-	{arity, 3} ->
-	    catch Fun(User, Peer, Info);
-	_ ->
-	    ok
-    catch
-	_:_ ->
-	    ok
-    end.
-
 %%%----------------------------------------------------------------
 %%% Cache idle timer that closes the connection if there are no
 %%% channels open for a while.
@@ -2511,6 +2104,10 @@ cond_set_idle_timer(D) ->
         _ -> {{timeout,idle_time}, infinity, none}
     end.
 
+channel_close_timer(D, ChannelId) ->
+    {{timeout, {channel_close, ChannelId}},
+     ?GET_OPT(channel_close_timeout, (D#data.ssh_params)#ssh.opts), none}.
+
 %%%----------------------------------------------------------------
 start_channel_request_timer(_,_, infinity) ->
     ok;
@@ -2518,43 +2115,17 @@ start_channel_request_timer(Channel, Fro
     erlang:send_after(Time, self(), {timeout, {Channel, From}}).
 
 %%%----------------------------------------------------------------
-%%% Connection start and initalization helpers
 
-socket_control(Socket, Pid, Options) ->
-    {_, Callback, _} =	?GET_OPT(transport, Options),
-    case Callback:controlling_process(Socket, Pid) of
-	ok ->
-	    gen_statem:cast(Pid, socket_control);
-	{error, Reason}	->
-	    {error, Reason}
-    end.
-
-handshake(Pid, Ref, Timeout) ->
-    receive
-	ssh_connected ->
-	    erlang:demonitor(Ref),
-	    {ok, Pid};
-	{Pid, not_connected, Reason} ->
-	    {error, Reason};
-	{Pid, user_password} ->
-	    Pass = io:get_password(),
-	    Pid ! Pass,
-	    handshake(Pid, Ref, Timeout);
-	{Pid, question} ->
-	    Answer = io:get_line(""),
-	    Pid ! Answer,
-	    handshake(Pid, Ref, Timeout);
-	{'DOWN', _, process, Pid, {shutdown, Reason}} ->
-	    {error, Reason};
-	{'DOWN', _, process, Pid, Reason} ->
-	    {error, Reason};
-        {'EXIT',_,Reason} ->
-            stop(Pid),
-            {error, {exit,Reason}}
-    after Timeout ->
-	    stop(Pid),
-	    {error, timeout}
-    end.
+init_inet_buffers_window(Socket) ->
+    %% Initialize the inet buffer handling. First try to increase the buffers:
+    update_inet_buffers(Socket),
+    %% then get good start values for the window handling:
+    {ok,SockOpts} = inet:getopts(Socket, [buffer,recbuf]),
+    WinSz = proplists:get_value(recbuf, SockOpts, ?DEFAULT_WINDOW_SIZE),
+    PktSz = min(proplists:get_value(buffer, SockOpts, ?DEFAULT_PACKET_SIZE),
+                ?DEFAULT_PACKET_SIZE),  % Too large packet size might cause deadlock
+                                        % between sending and receiving
+    {WinSz, PktSz}.
 
 update_inet_buffers(Socket) ->
     try
@@ -2564,7 +2135,11 @@ update_inet_buffers(Socket) ->
                          Val < MinVal]
     of
 	[] -> ok;
-	NewOpts -> inet:setopts(Socket, NewOpts)
+	NewOpts ->
+            inet:setopts(Socket, NewOpts),
+            %% Note that buffers might be of different size than we just requested,
+            %% the OS has the last word.
+            ok
     catch
         _:_ -> ok
     end.
@@ -2574,17 +2149,18 @@ update_inet_buffers(Socket) ->
 %%%# Tracing
 %%%#
 
-ssh_dbg_trace_points() -> [terminate, disconnect, connections, connection_events,
-                           connection_handshake, renegotiation].
+ssh_dbg_trace_points() -> [terminate, disconnect, connections, connection_events, renegotiation,
+                           tcp, connection_handshake].
 
 ssh_dbg_flags(connections) -> [c | ssh_dbg_flags(terminate)];
 ssh_dbg_flags(renegotiation) -> [c];
 ssh_dbg_flags(connection_events) -> [c];
 ssh_dbg_flags(connection_handshake) -> [c];
 ssh_dbg_flags(terminate) -> [c];
+ssh_dbg_flags(tcp) -> [c];
 ssh_dbg_flags(disconnect) -> [c].
 
-ssh_dbg_on(connections) -> dbg:tp(?MODULE,  init_connection_handler, 3, x),
+ssh_dbg_on(connections) -> dbg:tp(?MODULE,  init, 1, x),
                            ssh_dbg_on(terminate);
 ssh_dbg_on(connection_events) -> dbg:tp(?MODULE,   handle_event, 4, x);
 ssh_dbg_on(connection_handshake) -> dbg:tpl(?MODULE, handshake, 3, x);
@@ -2594,11 +2170,22 @@ ssh_dbg_on(renegotiation) -> dbg:tpl(?MO
                              dbg:tpl(?MODULE,   start_rekeying, 2, x),
                              dbg:tp(?MODULE,   renegotiate, 1, x);
 ssh_dbg_on(terminate) -> dbg:tp(?MODULE,  terminate, 3, x);
+ssh_dbg_on(tcp) -> dbg:tp(?MODULE, handle_event, 4,
+                          [{[info, {tcp,'_','_'},       '_', '_'], [], []},
+                           {[info, {tcp_error,'_','_'}, '_', '_'], [], []},
+                           {[info, {tcp_closed,'_'},    '_', '_'], [], []}
+                          ]),
+                   dbg:tp(?MODULE, send_bytes, 2, x),
+                   dbg:tpl(?MODULE, close_transport, 1, x);
+
 ssh_dbg_on(disconnect) -> dbg:tpl(?MODULE,  send_disconnect, 7, x).
 
 
 ssh_dbg_off(disconnect) -> dbg:ctpl(?MODULE, send_disconnect, 7);
 ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 3);
+ssh_dbg_off(tcp) -> dbg:ctpg(?MODULE, handle_event, 4), % How to avoid cancelling 'connection_events' ?
+                    dbg:ctpl(?MODULE, send_bytes, 2),
+                    dbg:ctpl(?MODULE, close_transport, 1);
 ssh_dbg_off(renegotiation) -> dbg:ctpl(?MODULE,   init_renegotiate_timers, 3),
                               dbg:ctpl(?MODULE,   pause_renegotiate_timers, 3),
                               dbg:ctpl(?MODULE,   check_data_rekeying_dbg, 2),
@@ -2606,11 +2193,11 @@ ssh_dbg_off(renegotiation) -> dbg:ctpl(?
                               dbg:ctpg(?MODULE,   renegotiate, 1);
 ssh_dbg_off(connection_events) -> dbg:ctpg(?MODULE, handle_event, 4);
 ssh_dbg_off(connection_handshake) -> dbg:ctpl(?MODULE, handshake, 3);
-ssh_dbg_off(connections) -> dbg:ctpg(?MODULE, init_connection_handler, 3),
+ssh_dbg_off(connections) -> dbg:ctpg(?MODULE, init, 1),
                             ssh_dbg_off(terminate).
 
 
-ssh_dbg_format(connections, {call, {?MODULE,init_connection_handler, [Role, Sock, Opts]}}) ->
+ssh_dbg_format(connections, {call, {?MODULE,init, [[Role, Sock, Opts]]}}) ->
     DefaultOpts = ssh_options:handle_options(Role,[]),
     ExcludedKeys = [internal_options, user_options],
     NonDefaultOpts =
@@ -2626,9 +2213,11 @@ ssh_dbg_format(connections, {call, {?MOD
     {ok, {IPp,Portp}} = inet:peername(Sock),
     {ok, {IPs,Ports}} = inet:sockname(Sock),
     [io_lib:format("Starting ~p connection:\n",[Role]),
-     io_lib:format("Socket = ~p, Peer = ~s:~p, Local = ~s:~p,~n"
+     io_lib:format("Socket = ~p, Peer = ~s, Local = ~s,~n"
                    "Non-default options:~n~p",
-                   [Sock,inet:ntoa(IPp),Portp,inet:ntoa(IPs),Ports,
+                   [Sock,
+                    ssh_lib:format_address_port(IPp,Portp),
+                    ssh_lib:format_address_port(IPs,Ports),
                     NonDefaultOpts])
     ];
 ssh_dbg_format(connections, F) ->
@@ -2643,11 +2232,48 @@ ssh_dbg_format(connection_events, {retur
      io_lib:format("~p~n", [ssh_dbg:reduce_state(Ret, #data{})])
     ];
 
+ssh_dbg_format(tcp, {call, {?MODULE,handle_event, [info, {tcp,Sock,TcpData}, State, _Data]}}) ->
+    ["TCP stream data arrived\n",
+     io_lib:format("State: ~p~n"
+                   "Socket: ~p~n"
+                   "TcpData:~n~s", [State, Sock, ssh_dbg:hex_dump(TcpData, [{max_bytes,48}])])
+    ];
+ssh_dbg_format(tcp, {call, {?MODULE,handle_event, [info, {tcp_error,Sock,Msg}, State, _Data]}}) ->
+    ["TCP stream data ERROR arrived\n",
+     io_lib:format("State: ~p~n"
+                   "Socket: ~p~n"
+                   "ErrorMsg:~p~n", [State, Sock, Msg])
+    ];
+ssh_dbg_format(tcp, {call, {?MODULE,handle_event, [info, {tcp_closed,Sock}, State, _Data]}}) ->
+    ["TCP stream closed\n",
+     io_lib:format("State: ~p~n"
+                   "Socket: ~p~n", [State, Sock])
+    ];
+ssh_dbg_format(tcp, {return_from, {?MODULE,handle_event,4}, _Ret}) ->
+    skip;
+
+ssh_dbg_format(tcp, {call, {?MODULE, send_bytes, ["",_D]}}) ->
+    skip;
+ssh_dbg_format(tcp, {call, {?MODULE, send_bytes, [TcpData, #data{socket=Sock}]}}) ->
+    ["TCP send stream data\n",
+     io_lib:format("Socket: ~p~n"
+                   "TcpData:~n~s", [Sock, ssh_dbg:hex_dump(TcpData, [{max_bytes,48}])])
+    ];
+ssh_dbg_format(tcp, {return_from, {?MODULE,send_bytes,2}, _R}) ->
+    skip;
+
+ssh_dbg_format(tcp, {call, {?MODULE, close_transport, [#data{socket=Sock}]}}) ->
+    ["TCP close stream\n",
+     io_lib:format("Socket: ~p~n", [Sock])
+    ];
+ssh_dbg_format(tcp, {return_from, {?MODULE,close_transport,1}, _R}) ->
+    skip;
+
 ssh_dbg_format(renegotiation, {call, {?MODULE,init_renegotiate_timers,[OldState,NewState,D]}}) ->
     ["Renegotiation: start timer (init_renegotiate_timers)\n",
      io_lib:format("State: ~p  -->  ~p~n"
                    "rekey_limit: ~p ({ms,bytes})~n"
-                   "check_data_size: ~p (ms)~n", 
+                   "check_data_size: ~p (ms)~n",
                    [OldState, NewState,
                     ?GET_OPT(rekey_limit, (D#data.ssh_params)#ssh.opts),
                     ?REKEY_DATA_TIMOUT])
@@ -2657,7 +2283,7 @@ ssh_dbg_format(renegotiation, {return_fr
 
 ssh_dbg_format(renegotiation, {call, {?MODULE,renegotiate,[ConnectionHandler]}}) ->
     ["Renegotiation: renegotiation forced\n",
-     io_lib:format("~p:renegotiate(~p) called~n", 
+     io_lib:format("~p:renegotiate(~p) called~n",
                    [?MODULE,ConnectionHandler])
     ];
 ssh_dbg_format(renegotiation, {return_from, {?MODULE,renegotiate,1}, _Ret}) ->
@@ -2733,7 +2359,6 @@ ssh_dbg_format(renegotiation, {return_fr
     skip.
 
 
-
 ssh_dbg_format(connection_handshake, {call, {?MODULE,handshake,[Pid, Ref, Timeout]}}, Stack) ->
     {["Connection handshake\n",
       io_lib:format("Connection Child: ~p~nReg: ~p~nTimeout: ~p~n",
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_connection_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_connection_sup.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_connection_sup.erl
@@ -1,8 +1,8 @@
 %%
 %% %CopyrightBegin%
-%% 
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
-%% 
+%%
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
+%%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
 %% You may obtain a copy of the License at
@@ -14,21 +14,24 @@
 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
-%% 
+%%
 %% %CopyrightEnd%
 %%
 %%
 %%----------------------------------------------------------------------
-%% Purpose: Ssh connection supervisor.
+%% Purpose: The ssh connection supervisor
 %%----------------------------------------------------------------------
 
 -module(ssh_connection_sup).
 
 -behaviour(supervisor).
 
-%% API
--export([start_link/1]).
--export([start_child/2]).
+-include("ssh.hrl").
+
+-export([start_link/4,
+         start_channel/8,
+         tcpip_fwd_supervisor/1
+	]).
 
 %% Supervisor callback
 -export([init/1]).
@@ -36,23 +39,67 @@
 %%%=========================================================================
 %%%  API
 %%%=========================================================================
-start_link(Args) ->
-    supervisor:start_link(?MODULE, [Args]).
+start_link(Role, Id, Socket, Options) ->
+    case supervisor:start_link(?MODULE, [Role, Id, Socket, Options]) of
+        {error, {shutdown, {failed_to_start_child, _, Error}}} ->
+            {error,Error};
+        Other ->
+            Other
+    end.
+
+start_channel(Role, SupPid, ConnRef, Callback, Id, Args, Exec, Opts) ->
+    ChannelSup = channel_supervisor(SupPid),
+    ssh_channel_sup:start_child(Role, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts).
+
+tcpip_fwd_supervisor(ConnectionSup) ->
+    find_child(tcpip_forward_acceptor_sup, ConnectionSup).
 
-start_child(Sup, Args) ->
-    supervisor:start_child(Sup, Args).
 
 %%%=========================================================================
 %%%  Supervisor callback
 %%%=========================================================================
-init(_) ->
-    SupFlags = #{strategy  => simple_one_for_one, 
-                 intensity =>    0,
-                 period    => 3600
-                },
-    ChildSpecs = [#{id       => undefined, % As simple_one_for_one is used.
-                    start    => {ssh_connection_handler, start_link, []},
-                    restart  => temporary % because there is no way to restart a crashed connection
-                   }
-                 ],
+init([Role, Id, Socket, Options]) ->
+    ConnectionSup = self(),
+    SupFlags = #{strategy      => one_for_all,
+                 auto_shutdown => any_significant,
+                 intensity     =>    0,
+                 period        => 3600},
+    ChildSpecs =
+        [#{id          => connection,
+           restart     => temporary,
+           type        => worker,
+           significant => true,
+           start       => {ssh_connection_handler,
+                           start_link,
+                           [Role, Id, Socket,
+                            ?PUT_INTERNAL_OPT([{connection_sup, ConnectionSup}], Options)]}
+          },
+         #{id      => channel_sup,
+           restart => temporary,
+           type    => supervisor,
+           start   => {ssh_channel_sup, start_link, [Options]}
+          },
+
+         #{id      => tcpip_forward_acceptor_sup,
+           restart => temporary,
+           type    => supervisor,
+           start   => {ssh_tcpip_forward_acceptor_sup, start_link, []}
+          }],
     {ok, {SupFlags,ChildSpecs}}.
+
+%%%=========================================================================
+%%%  Internal functions
+%%%=========================================================================
+channel_supervisor(ConnectionSup) -> find_child(channel_sup, ConnectionSup).
+
+find_child(Id, Sup) when is_pid(Sup) ->
+    try
+       {Id, Pid, _, _} = lists:keyfind(Id, 1, supervisor:which_children(Sup)),
+       Pid
+    catch
+        exit:{no_proc,_} ->
+            {error, no_proc};
+        _:_ ->
+            {error, {id_not_found,?MODULE,Id}}
+    end.
+
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh.erl
@@ -1,7 +1,7 @@
 %
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
 	 channel_info/3,
 	 daemon/1, daemon/2, daemon/3,
 	 daemon_info/1, daemon_info/2,
+         daemon_replace_options/2,
          set_sock_opts/2, get_sock_opts/2,
 	 default_algorithms/0,
          chk_algos_opts/1,
@@ -45,6 +46,17 @@
          tcpip_tunnel_to_server/5, tcpip_tunnel_to_server/6
 	]).
 
+%% In move from public_key
+-export([hostkey_fingerprint/1, hostkey_fingerprint/2
+        ]).
+         
+
+%%% Internal export
+-export([is_host/2]).
+
+-behaviour(ssh_dbg).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2, ssh_dbg_format/3]).
+
 %%% "Deprecated" types export:
 -export_type([ssh_daemon_ref/0, ssh_connection_ref/0, ssh_channel_id/0]).
 -opaque ssh_daemon_ref()     :: daemon_ref().
@@ -97,7 +109,7 @@ start(Type) ->
         {ok, _} ->
             %% Clear cached default_algorithms (if exists) ...
             ssh_transport:clear_default_algorithms_env(),
-            %% ... and rebuld them taking configure options in account
+            %% ... and rebuild them taking configure options in account
             ssh_transport:default_algorithms(),
             ok;
         Other ->
@@ -115,90 +127,128 @@ stop() ->
 %%--------------------------------------------------------------------
 %% Description: Starts an ssh connection.
 %%--------------------------------------------------------------------
--spec connect(OpenTcpSocket, Options) -> {ok,connection_ref()} | {error,term()} when
+-define(IS_VALID_OPTIONS(Options), is_list(Options)).
+-define(IS_VALID_PORT(Port), (is_integer(Port) andalso Port > 0)).
+-define(IS_VALID_TIMEOUT(Timeout),
+        (Timeout == infinity
+         orelse (is_integer(Timeout)
+                 andalso Timeout >= 0))).
+
+-spec connect(OpenTcpSocket, Options)
+             -> {ok, connection_ref()}
+              | {error, term()} when
       OpenTcpSocket :: open_socket(),
       Options :: client_options().
 
-connect(OpenTcpSocket, Options) when is_port(OpenTcpSocket),
-                                     is_list(Options) ->
-    connect(OpenTcpSocket, Options, infinity).
-
+connect(OpenTcpSocket, Options) when ?IS_VALID_OPTIONS(Options) ->
+    connect(OpenTcpSocket, Options, infinity);
+connect(_OpenTcpSocket, Options) ->
+    bad_arg([{options, Options}]).
 
 -spec connect(open_socket(), client_options(), timeout()) ->
-                     {ok,connection_ref()} | {error,term()}
+                     {ok, connection_ref()} | {error, term()}
            ; (host(), inet:port_number(), client_options()) ->
-                     {ok,connection_ref()} | {error,term()}.
+                     {ok, connection_ref()} | {error, term()}.
 
-connect(Socket, UserOptions, NegotiationTimeout) when is_port(Socket),
-                                                      is_list(UserOptions) ->
+connect(Host, Port, Options) when ?IS_VALID_PORT(Port),
+                                  ?IS_VALID_OPTIONS(Options) ->
+    Timeout = proplists:get_value(connect_timeout, Options, infinity),
+    connect(Host, Port, Options, Timeout);
+connect(Socket, UserOptions, NegotiationTimeout)
+  when ?IS_VALID_OPTIONS(UserOptions),
+       ?IS_VALID_TIMEOUT(NegotiationTimeout) ->
     case ssh_options:handle_options(client, UserOptions) of
 	{error, Error} ->
 	    {error, Error};
-	Options ->
-           case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
-               ok ->
-                   connect_socket(Socket,
-                                  ?PUT_INTERNAL_OPT({connected_socket,Socket}, Options),
-                                  NegotiationTimeout);
-               {error,SockError} ->
-                   {error,SockError}
-           end
-        end;
-
-connect(Host, Port, Options) when is_integer(Port),
-                                  Port>0,
-                                  is_list(Options) ->
-    Timeout = proplists:get_value(connect_timeout, Options, infinity),
-    connect(Host, Port, Options, Timeout).
 
+	Options = #{} ->
+            case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
+                ok ->
+                    continue_connect(Socket, Options, NegotiationTimeout);
+                {error,SockError} ->
+                    {error,SockError}
+            end
+    end;
+connect(_HostOrSocket, PortOrOptions, OptionsOrTimeout) ->
+    bad_arg(PortOrOptions, OptionsOrTimeout).
 
--spec connect(Host, Port, Options, NegotiationTimeout) -> {ok,connection_ref()} | {error,term()} when
+-spec connect(Host, Port, Options, NegotiationTimeout)
+             -> {ok, connection_ref()}
+              | {error, term()} when
       Host :: host(),
       Port :: inet:port_number(),
       Options :: client_options(),
       NegotiationTimeout :: timeout().
 
-connect(Host0, Port, UserOptions, NegotiationTimeout) when is_integer(Port),
-                                                           Port>0,
-                                                           is_list(UserOptions) ->
+connect(Host0, Port, UserOptions, NegotiationTimeout)
+  when ?IS_VALID_PORT(Port),
+       ?IS_VALID_OPTIONS(UserOptions),
+       ?IS_VALID_TIMEOUT(NegotiationTimeout) ->
     case ssh_options:handle_options(client, UserOptions) of
-	{error, _Reason} = Error ->
-	    Error;
+	{error, Reason} ->
+            {error, Reason};
+
         Options ->
-	    {_, Transport, _} = TransportOpts = ?GET_OPT(transport, Options),
-	    ConnectionTimeout = ?GET_OPT(connect_timeout, Options),
-            SocketOpts = [{active,false} | ?GET_OPT(socket_options,Options)],
-            Host = mangle_connect_address(Host0, SocketOpts),
-	    try Transport:connect(Host, Port, SocketOpts, ConnectionTimeout) of
-		{ok, Socket} ->
-                    connect_socket(Socket,
-                                   ?PUT_INTERNAL_OPT({host,Host}, Options),
-                                   NegotiationTimeout);
-		{error, Reason} ->
-		    {error, Reason}
-	    catch
-		exit:{function_clause, _F} ->
-		    {error, {options, {transport, TransportOpts}}};
-		exit:badarg ->
-		    {error, {options, {socket_options, SocketOpts}}}
-	    end
+            SocketOpts = ?GET_OPT(socket_options,Options) ++ [{active,false}],
+            Host = mangle_connect_address(Host0, Options),
+            try
+                transport_connect(Host, Port, SocketOpts, Options)
+            of
+                {ok, Socket} ->
+                    continue_connect(Socket, Options, NegotiationTimeout);
+                {error, Reason} ->
+                    {error, Reason}
+            catch
+                _:badarg -> {error, {options,?GET_OPT(socket_options,Options)}};
+                _:{error,Reason} -> {error,Reason};
+                error:Error -> {error,Error};
+                Class:Error -> {error, {Class,Error}}
+            end
+    end;
+connect(_Host, Port, UserOptions, NegotiationTimeout) ->
+    bad_arg([{port, Port},
+             {options, UserOptions},
+             {timeout, NegotiationTimeout}]).
+
+bad_arg(Args) ->
+    hd(bad_args(Args)).
+
+%% Special handling for finding the incorrect args for connect/3,
+%% which has two distinctly different signatures.
+bad_arg(Arg2, Arg3) ->
+    E0 = bad_args([{port, Arg2}, {options, Arg3}]),
+    E1 = bad_args([{options, Arg2}, {timeout, Arg3}]),
+    %% Select the case with only one error
+    case {E0, E1} of
+        {[Error], _}    -> Error;
+        {_, [Error]}    -> Error;
+        {[Error, _], _} -> Error
     end.
 
+%% Return list of errors
+-spec bad_args([{'options' | 'port' | 'timeout', any()}]) ->
+          [{'error', term()}].
+bad_args(Args) ->
+    IsErr = fun(true, _) -> false;
+               (false, Error) -> {true, {error, Error}}
+            end,
+    Check =
+        fun({options, Arg}) -> IsErr(?IS_VALID_OPTIONS(Arg), invalid_options);
+           ({timeout, Arg}) -> IsErr(?IS_VALID_TIMEOUT(Arg), invalid_timeout);
+           ({port, Arg})    -> IsErr(?IS_VALID_PORT(Arg), invalid_port)
+        end,
 
-connect_socket(Socket, Options0, NegotiationTimeout) ->
-    {ok, {Host,Port}} = inet:sockname(Socket),
-    Profile = ?GET_OPT(profile, Options0),
-
-    {ok, {SystemSup, SubSysSup}} = sshc_sup:start_system_subsystem(Host, Port, Profile, Options0),
-
-    ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
-    Opts = ?PUT_INTERNAL_OPT([{user_pid,self()},
-                              {supervisors, [{system_sup, SystemSup},
-                                             {subsystem_sup, SubSysSup},
-                                             {connection_sup, ConnectionSup}]}
-                             ], Options0),
-    ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout).
+    lists:filtermap(Check, Args).
 
+%%%----------------
+continue_connect(Socket, Options0, NegTimeout) ->
+    {ok, {SockHost,SockPort}} = inet:sockname(Socket),
+    Options = ?PUT_INTERNAL_OPT([{negotiation_timeout,NegTimeout}], Options0),
+    Address = #address{address = SockHost,
+                       port = SockPort,
+                       profile = ?GET_OPT(profile,Options)
+                      },
+    ssh_system_sup:start_connection(client, Address, Socket, Options).
 
 %%--------------------------------------------------------------------
 -spec close(ConnectionRef) -> ok | {error,term()} when
@@ -237,16 +287,18 @@ close(ConnectionRef) ->
       | {options, client_options()}
       | {algorithms, conn_info_algs()}
       | {channels, conn_info_channels()}.
-        
--spec connection_info(ConnectionRef) -> InfoTupleList when
+
+-spec connection_info(ConnectionRef) ->
+          InfoTupleList | {error, term()} when
       ConnectionRef :: connection_ref(),
       InfoTupleList :: [InfoTuple],
       InfoTuple :: connection_info_tuple().
 
-connection_info(ConnectionRef) ->                                      
+connection_info(ConnectionRef) ->
     connection_info(ConnectionRef, []).
 
--spec connection_info(ConnectionRef, ItemList|Item) ->  InfoTupleList|InfoTuple when
+-spec connection_info(ConnectionRef, ItemList|Item) ->
+          InfoTupleList | InfoTuple | {error, term()} when
       ConnectionRef :: connection_ref(),
       ItemList :: [Item],
       Item :: client_version | server_version | user | peer | sockname | options | algorithms | sockname,
@@ -276,46 +328,48 @@ daemon(Port) ->
 
 -spec daemon(inet:port_number()|open_socket(), daemon_options()) -> {ok,daemon_ref()} | {error,term()}.
 
-daemon(Socket, UserOptions) when is_port(Socket) ->
-    try
-        #{} = Options = ssh_options:handle_options(server, UserOptions),
+daemon(Port, UserOptions) when 0 =< Port,Port =< 65535 ->
+    daemon(any, Port, UserOptions);
 
-        case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
-            ok ->
-                {ok, {IP,Port}} = inet:sockname(Socket),
-                finalize_start(IP, Port, ?GET_OPT(profile, Options),
-                               ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options),
-                               fun(Opts, DefaultResult) ->
-                                       try ssh_acceptor:handle_established_connection(
-                                             IP, Port, Opts, Socket)
-                                       of
-                                           {error,Error} ->
-                                               {error,Error};
-                                           _ ->
-                                               DefaultResult
-                                       catch
-                                           C:R ->
-                                               {error,{could_not_start_connection,{C,R}}}
-                                       end
-                               end);
-            {error,SockError} ->
-                {error,SockError}
-            end
-    catch
-        throw:bad_fd ->
-            {error,bad_fd};
-        throw:bad_socket ->
-            {error,bad_socket};
-        error:{badmatch,{error,Error}} ->
-            {error,Error};
-        error:Error ->
-            {error,Error};
-        _C:_E ->
-            {error,{cannot_start_daemon,_C,_E}}
-    end;
+daemon(Socket, UserOptions) ->
+    case ssh_options:handle_options(server, UserOptions) of
+        #{} = Options0 ->
+            case valid_socket_to_use(Socket, ?GET_OPT(transport,Options0)) of
+                ok ->
+                    try
+                        %% throws error:Error if no usable hostkey is found
+                        ssh_connection_handler:available_hkey_algorithms(server, Options0),
+                        {ok, {SockHost,SockPort}} = inet:sockname(Socket),
+                        Address = #address{address = SockHost,
+                                           port = SockPort,
+                                           profile = ?GET_OPT(profile,Options0)
+                                          },
+                        Options = ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options0),
+                        case ssh_system_sup:start_connection(server, Address, Socket, Options) of
+                            {ok,Pid} ->
+                                {ok,Pid};
+                            {error, {already_started, _}} ->
+                                {error, eaddrinuse};
+                            {error, Error} ->
+                                {error, Error}
+                        end
+                    catch
+                        error:{shutdown,Err} ->
+                            {error,Err};
+                        exit:{noproc, _} ->
+                            {error, ssh_not_started};
+                        C:R ->
+                            {error,{could_not_start_connection,{C,R}}}
+                    end;
+
+                {error,SockError} ->
+                    {error,SockError}
+            end;
+
+        {error,OptionError} ->
+            {error,OptionError}
+    end.
 
-daemon(Port, UserOptions) when 0 =< Port, Port =< 65535 ->
-    daemon(any, Port, UserOptions).
 
 
 -spec daemon(any | inet:ip_address(), inet:port_number(), daemon_options()) -> {ok,daemon_ref()} | {error,term()}
@@ -327,31 +381,46 @@ daemon(Host0, Port0, UserOptions0) when
     try
         {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0),
         #{} = Options0 = ssh_options:handle_options(server, UserOptions),
-        {open_listen_socket(Host1, Port0, Options0), Options0}
+        %% We need to open the listen socket here before start of the system supervisor. That
+        %% is because Port0 might be 0, or if an FD is provided in the Options0, in which case
+        %% the real listening port will be known only after the gen_tcp:listen call.
+        maybe_open_listen_socket(Host1, Port0, Options0)
     of
-        {{{Host,Port}, ListenSocket}, Options1} ->
+        {Host, Port, ListenSocket, Options1} ->
             try
                 %% Now Host,Port is what to use for the supervisor to register its name,
-                %% and ListenSocket is for listening on connections. But it is still owned
-                %% by self()...
-                finalize_start(Host, Port, ?GET_OPT(profile, Options1),
-                               ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options1),
-                               fun(Opts, Result) ->
-                                       {_, Callback, _} = ?GET_OPT(transport, Opts),
-                                       receive
-                                           {request_control, ListenSocket, ReqPid} ->
-                                               ok = Callback:controlling_process(ListenSocket, ReqPid),
-                                               ReqPid ! {its_yours,ListenSocket},
-                                               Result
-                                       end
-                               end)
+                %% and ListenSocket, if provided,  is for listening on connections. But
+                %% it is still owned by self()...
+
+                %% throws error:Error if no usable hostkey is found
+                ssh_connection_handler:available_hkey_algorithms(server, Options1),
+                ssh_system_sup:start_system(#address{address = Host,
+                                                     port = Port,
+                                                     profile = ?GET_OPT(profile,Options1)},
+                                            Options1)
             of
-                {error,Err} ->
+                {ok,DaemonRef} when ListenSocket == undefined ->
+                    {ok,DaemonRef};
+                {ok,DaemonRef} ->
+                    receive
+                        {request_control, ListenSocket, ReqPid} ->
+                            ok = controlling_process(ListenSocket, ReqPid, Options1),
+                            ReqPid ! {its_yours,ListenSocket}
+                    end,
+                    {ok,DaemonRef};
+                {error, {already_started, _}} ->
                     close_listen_socket(ListenSocket, Options1),
-                    {error,Err};
-                OK ->
-                    OK
+                    {error, eaddrinuse};
+                {error, Error} ->
+                    close_listen_socket(ListenSocket, Options1),
+                    {error, Error}
             catch
+                error:{shutdown,Err} ->
+                    close_listen_socket(ListenSocket, Options1),
+                    {error,Err};
+                exit:{noproc, _} ->
+                    close_listen_socket(ListenSocket, Options1),
+                    {error, ssh_not_started};
                 error:Error ->
                     close_listen_socket(ListenSocket, Options1),
                     error(Error);
@@ -376,6 +445,21 @@ daemon(_, _, _) ->
     {error, badarg}.
 
 %%--------------------------------------------------------------------
+-spec daemon_replace_options(DaemonRef, NewUserOptions) -> {ok,daemon_ref()}
+                                                         | {error,term()} when
+      DaemonRef :: daemon_ref(),
+      NewUserOptions :: daemon_options().
+
+daemon_replace_options(DaemonRef, NewUserOptions) ->
+    case ssh_system_sup:get_acceptor_options(DaemonRef) of
+        {ok, Os0} ->
+            Os1 = ssh_options:merge_options(server, NewUserOptions, Os0),
+            ssh_system_sup:replace_acceptor_options(DaemonRef, Os1);
+        {error, _Reason} = Error ->
+            Error
+    end.
+
+%%--------------------------------------------------------------------
 -type daemon_info_tuple() ::
         {port, inet:port_number()}
       | {ip, inet:ip_address()}
@@ -388,20 +472,16 @@ daemon(_, _, _) ->
       InfoTuple :: daemon_info_tuple().
 
 daemon_info(DaemonRef) ->
-    case catch ssh_system_sup:acceptor_supervisor(DaemonRef) of
-	AsupPid when is_pid(AsupPid) ->
-	    [{Host,Port,Profile}] =
-		[{Hst,Prt,Prf} 
-                 || {{ssh_acceptor_sup,Hst,Prt,Prf},_Pid,worker,[ssh_acceptor]} 
-                        <- supervisor:which_children(AsupPid)],
-            IP =
-                case inet:parse_strict_address(Host) of
-                    {ok,IP0} -> IP0;
-                    _ -> Host
+    case ssh_system_sup:get_daemon_listen_address(DaemonRef) of
+        {ok,A} ->
+            Address =
+                case inet:parse_strict_address(A#address.address) of
+                    {ok,IP} -> A#address{address=IP};
+                    _ -> A
                 end,
-
             Opts =
-                case ssh_system_sup:get_options(DaemonRef, Host, Port, Profile) of
+                %% Pick a subset of the Options to present:
+                case ssh_system_sup:get_options(DaemonRef, Address) of
                     {ok, OptMap} ->
                         lists:sort(
                           maps:to_list(
@@ -412,11 +492,12 @@ daemon_info(DaemonRef) ->
                         []
                 end,
             
-	    {ok, [{port,Port},
-                  {ip,IP},
-                  {profile,Profile},
-                  {options,Opts}
+	    {ok, [{port,    Address#address.port},
+                  {ip,      Address#address.address},
+                  {profile, Address#address.profile},
+                  {options, Opts}
                  ]};
+        
 	_ ->
 	    {error,bad_daemon_ref}
     end.
@@ -460,14 +541,13 @@ stop_listener(Address, Port) ->
 
 -spec stop_listener(any|inet:ip_address(), inet:port_number(), term()) -> ok.
 
-stop_listener(any, Port, Profile) ->
-    map_ip(fun(IP) ->
-                   ssh_system_sup:stop_listener(IP, Port, Profile) 
-           end, [{0,0,0,0},{0,0,0,0,0,0,0,0}]);
 stop_listener(Address, Port, Profile) ->
-    map_ip(fun(IP) ->
-                   ssh_system_sup:stop_listener(IP, Port, Profile) 
-           end, {address,Address}).
+    lists:foreach(fun({Sup,_Addr}) ->
+                          stop_listener(Sup)
+                  end,
+                  ssh_system_sup:addresses(#address{address=Address,
+                                                    port=Port,
+                                                    profile=Profile})).
 
 %%--------------------------------------------------------------------
 %% Description: Stops the listener and all connections started by
@@ -476,7 +556,7 @@ stop_listener(Address, Port, Profile) ->
 -spec stop_daemon(DaemonRef::daemon_ref()) -> ok.
 
 stop_daemon(SysSup) ->
-    ssh_system_sup:stop_system(server, SysSup).
+    ssh_system_sup:stop_system(SysSup).
 
 
 -spec stop_daemon(inet:ip_address(), inet:port_number()) -> ok.
@@ -487,14 +567,13 @@ stop_daemon(Address, Port) ->
 
 -spec stop_daemon(any|inet:ip_address(), inet:port_number(), atom()) -> ok.
 
-stop_daemon(any, Port, Profile) ->
-    map_ip(fun(IP) ->
-                   ssh_system_sup:stop_system(server, IP, Port, Profile) 
-           end, [{0,0,0,0},{0,0,0,0,0,0,0,0}]);
 stop_daemon(Address, Port, Profile) ->
-    map_ip(fun(IP) ->
-                   ssh_system_sup:stop_system(server, IP, Port, Profile) 
-           end, {address,Address}).
+    lists:foreach(fun({Sup,_Addr}) ->
+                          stop_daemon(Sup)
+                  end,
+                  ssh_system_sup:addresses(#address{address=Address,
+                                                    port=Port,
+                                                    profile=Profile})).
 
 %%--------------------------------------------------------------------
 %% Description: Starts an interactive shell to an SSH server on the
@@ -504,9 +583,6 @@ stop_daemon(Address, Port, Profile) ->
 %%--------------------------------------------------------------------
 -spec shell(open_socket() | host() | connection_ref()) ->  _.
 
-shell(Socket) when is_port(Socket) ->
-    shell(Socket, []);
-
 shell(ConnectionRef) when is_pid(ConnectionRef) ->
     case ssh_connection:session_channel(ConnectionRef, infinity) of
 	{ok,ChannelId}  ->
@@ -529,23 +605,37 @@ shell(ConnectionRef) when is_pid(Connect
 	    Error
     end;
 
-shell(Host) ->
-    shell(Host, ?SSH_DEFAULT_PORT, []).
+shell(Dest) ->
+    case is_host(Dest, []) of
+        true ->
+            shell(Dest, ?SSH_DEFAULT_PORT, []);
+        false ->
+            %% Maybe socket
+            shell_socket(Dest, [])
+    end.
+
 
 
 -spec shell(open_socket() | host(), client_options()) ->  _.
 
-shell(Socket, Options) when is_port(Socket) ->
+shell(Dest, Options) ->
+    case is_host(Dest, Options) of
+        true ->
+            shell(Dest, ?SSH_DEFAULT_PORT, Options);
+        false ->
+            %% Maybe socket
+            shell_socket(Dest, Options)
+    end.
+
+shell_socket(Socket, Options) ->
     case connect(Socket, Options) of
         {ok,ConnectionRef} ->
             shell(ConnectionRef),
             close(ConnectionRef);
         Error ->
             Error
-    end;
-
-shell(Host, Options) ->
-    shell(Host, ?SSH_DEFAULT_PORT, Options).
+    end.
+    
 
 
 -spec shell(Host, Port, Options) -> _ when
@@ -716,6 +806,56 @@ tcpip_tunnel_from_server(ConnectionRef,
     end.
 
 %%--------------------------------------------------------------------
+%% In move from public_key
+%%--------------------------------------------------------------------
+-spec hostkey_fingerprint(public_key:public_key()) -> string().
+
+hostkey_fingerprint(Key) ->
+    sshfp_string(md5, ssh_message:ssh2_pubkey_encode(Key) ).
+
+-spec hostkey_fingerprint(TypeOrTypes, Key) -> StringOrString
+                                                   when
+      TypeOrTypes :: public_key:digest_type() | [public_key:digest_type()],
+      Key :: public_key:public_key(),
+      StringOrString :: string() | [string()] .
+
+hostkey_fingerprint(HashAlgs, Key) when is_list(HashAlgs) ->
+    EncKey = ssh_message:ssh2_pubkey_encode(Key),
+    [sshfp_full_string(HashAlg,EncKey) || HashAlg <- HashAlgs];
+hostkey_fingerprint(HashAlg, Key) when is_atom(HashAlg) ->
+    EncKey = ssh_message:ssh2_pubkey_encode(Key),
+    sshfp_full_string(HashAlg, EncKey).
+
+
+sshfp_string(HashAlg, EncodedKey) ->
+    %% Other HashAlgs than md5 will be printed with
+    %% other formats than hextstr by
+    %%    ssh-keygen -E <alg> -lf <file>
+    fp_fmt(sshfp_fmt(HashAlg), crypto:hash(HashAlg, EncodedKey)).
+
+sshfp_full_string(HashAlg, EncKey) ->
+    lists:concat([sshfp_alg_name(HashAlg),
+		  [$: | sshfp_string(HashAlg, EncKey)]
+		 ]).
+
+sshfp_alg_name(sha) -> "SHA1";
+sshfp_alg_name(Alg) -> string:to_upper(atom_to_list(Alg)).
+
+sshfp_fmt(md5) -> hexstr;
+sshfp_fmt(_) -> b64.
+
+fp_fmt(hexstr, Bin) ->
+    lists:flatten(string:join([io_lib:format("~2.16.0b",[C1]) || <<C1>> <= Bin], ":"));
+fp_fmt(b64, Bin) ->
+    %% This function clause *seems* to be
+    %%    [C || C<-base64:encode_to_string(Bin), C =/= $=]
+    %% but I am not sure. Must be checked.
+    B64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
+    BitsInLast = 8*byte_size(Bin) rem 6,
+    Padding = (6-BitsInLast) rem 6, % Want BitsInLast = [1:5] to map to padding [5:1] and 0 -> 0
+    [lists:nth(C+1,B64Chars) || <<C:6>> <= <<Bin/binary,0:Padding>> ].
+
+%%--------------------------------------------------------------------
 %%% Internal functions
 %%--------------------------------------------------------------------
 %% The handle_daemon_args/2 function basically only sets the ip-option in Opts
@@ -759,17 +899,26 @@ is_tcp_socket(Socket) ->
     end.
 
 %%%----------------------------------------------------------------
-open_listen_socket(_Host0, Port0, Options0) ->
-    {ok,LSock} =
-        case ?GET_SOCKET_OPT(fd, Options0) of
-            undefined ->
-                ssh_acceptor:listen(Port0, Options0);
+maybe_open_listen_socket(Host, Port, Options) ->
+    Opened =
+        case ?GET_SOCKET_OPT(fd, Options) of
+            undefined when Port == 0 ->
+                ssh_acceptor:listen(0, Options);
             Fd when is_integer(Fd) ->
                 %% Do gen_tcp:listen with the option {fd,Fd}:
-                ssh_acceptor:listen(0, Options0)
+                ssh_acceptor:listen(0, Options);
+            undefined ->
+                open_later
         end,
-    {ok,{LHost,LPort}} = inet:sockname(LSock),
-    {{LHost,LPort}, LSock}.
+    case Opened of
+        {ok,LSock} ->
+            {ok,{LHost,LPort}} = inet:sockname(LSock),
+            {LHost, LPort, LSock, ?PUT_INTERNAL_OPT({lsocket,{LSock,self()}}, Options)};
+        open_later ->
+            {Host, Port, undefined, Options};
+        Others ->
+            Others
+    end.
 
 %%%----------------------------------------------------------------
 close_listen_socket(ListenSocket, Options) ->
@@ -780,41 +929,32 @@ close_listen_socket(ListenSocket, Option
         _C:_E -> ok
     end.
 
+controlling_process(ListenSocket, ReqPid, Options) ->
+    {_, Callback, _} = ?GET_OPT(transport, Options),
+    Callback:controlling_process(ListenSocket, ReqPid).
+
+transport_connect(Host, Port, SocketOpts, Options) ->
+    {_, Callback, _} = ?GET_OPT(transport, Options),
+    Callback:connect(Host, Port, SocketOpts, ?GET_OPT(connect_timeout,Options)).
+    
 %%%----------------------------------------------------------------
-finalize_start(Host, Port, Profile, Options0, F) ->
-    try
-        %% throws error:Error if no usable hostkey is found
-        ssh_connection_handler:available_hkey_algorithms(server, Options0),
-
-        sshd_sup:start_child(Host, Port, Profile, Options0)
-    of
-        {error, {already_started, _}} ->
-            {error, eaddrinuse};
-        {error, Error} ->
-            {error, Error};
-        Result = {ok,_} ->
-            F(Options0, Result)
+is_host(X, Opts) ->
+    try is_host1(mangle_connect_address(X, Opts))
     catch
-        error:{shutdown,Err} ->
-            {error,Err};
-        exit:{noproc, _} ->
-            {error, ssh_not_started}
+        _:_ -> false
     end.
+            
 
-%%%----------------------------------------------------------------
-map_ip(Fun, {address,IP}) when is_tuple(IP) ->
-    Fun(IP);
-map_ip(Fun, {address,Address}) ->
-    IPs = try {ok,#hostent{h_addr_list=IP0s}} = inet:gethostbyname(Address),
-               IP0s
-          catch
-              _:_ -> []
-          end,
-    map_ip(Fun, IPs);
-map_ip(Fun, IPs) ->
-    lists:map(Fun, IPs).
+is_host1(L) when is_list(L) -> true; %% "string()"
+is_host1(T) when tuple_size(T)==4 -> lists:all(fun(I) -> 0=<I andalso I=<255 end,
+                                               tuple_to_list(T));
+is_host1(T) when tuple_size(T)==16 -> lists:all(fun(I) -> 0=<I andalso I=<65535 end,
+                                                tuple_to_list(T));
+is_host1(loopback) -> true.
 
 %%%----------------------------------------------------------------
+mangle_connect_address(A,  #{socket_options := SockOpts}) ->
+    mangle_connect_address(A, SockOpts);
 mangle_connect_address(A, SockOpts) ->
     mangle_connect_address1(A, proplists:get_value(inet6,SockOpts,false)).
 
@@ -845,3 +985,58 @@ mangle_tunnel_address(X) when is_list(X)
                                      {ok, {0,0,0,0,0,0,0,0}} -> <<"">>;
                                      _ -> list_to_binary(X)
                                  end.
+
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+
+ssh_dbg_trace_points() -> [tcp].
+
+ssh_dbg_flags(tcp) -> [c].
+
+ssh_dbg_on(tcp) -> dbg:tpl(?MODULE, controlling_process, 3, x),
+                   dbg:tpl(?MODULE, transport_connect, 4, x),
+                   dbg:tpl(?MODULE, close_listen_socket, 2, x).
+                   
+ssh_dbg_off(tcp) ->dbg:ctpl(?MODULE, controlling_process, 3),
+                   dbg:ctpl(?MODULE, transport_connect, 4),
+                   dbg:ctpl(?MODULE, close_listen_socket, 2).
+
+ssh_dbg_format(tcp, {call, {?MODULE,controlling_process, [ListenSocket, ReqPid, _Opts]}}) ->
+    ["TCP socket transferred to\n",
+     io_lib:format("Sock: ~p~n"
+                   "ToPid: ~p~n", [ListenSocket, ReqPid])
+    ];
+ssh_dbg_format(tcp, {return_from, {?MODULE,controlling_process,3}, _Result}) ->
+    skip;
+
+ssh_dbg_format(tcp, {call, {?MODULE,close_listen_socket, [ListenSocket, _Opts]}}) ->
+    ["TCP socket listening closed\n",
+     io_lib:format("Sock: ~p~n", [ListenSocket])
+    ];
+ssh_dbg_format(tcp, {return_from, {?MODULE,close_listen_socket,2}, _Result}) ->
+    skip.
+
+
+ssh_dbg_format(tcp, {call, {?MODULE,transport_connect, [Host,Port,SockOpts,_Opts]}}, Stack) ->
+    {skip, [{transport_connect,Host,Port,SockOpts}|Stack]};
+ssh_dbg_format(tcp, {return_from, {?MODULE,transport_connect,4}, {ok,Sock}},
+               [{transport_connect,Host,Port,SockOpts}|Stack]) ->
+    {["TCP connected to\n",
+      io_lib:format("Host: ~p~n"
+                    "Port: ~p~n"
+                    "SockOpts: ~p~n"
+                    "Socket: ~p~n", [Host,Port,SockOpts,Sock])
+     ],
+     Stack};
+ssh_dbg_format(tcp, {return_from, {?MODULE,transport_connect,4}, Result},
+               [{transport_connect,Host,Port,SockOpts}|Stack]) ->
+    {["TCP connected FAILED to\n",
+      io_lib:format("Host: ~p~n"
+                    "Port: ~p~n"
+                    "SockOpts: ~p~n"
+                    "Result: ~p~n", [Host,Port,SockOpts,Result])
+     ],
+     Stack}.
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh.hrl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh.hrl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh.hrl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -36,6 +36,8 @@
 
 -define(DEFAULT_SHELL, {shell, start, []} ).
 
+-define(DEFAULT_TIMEOUT, 5000).
+
 -define(MAX_RND_PADDING_LEN, 15).
 
 -define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
@@ -48,7 +50,7 @@
 -define(UINT16(X),   (X):16/unsigned-big-integer).
 -define(UINT32(X),   (X):32/unsigned-big-integer).
 -define(UINT64(X),   (X):64/unsigned-big-integer).
--define(STRING(X),   ?UINT32((size(X))), (X)/binary).
+-define(STRING(X),   ?UINT32((byte_size(X))), (X)/binary).
 
 -define(DEC_BIN(X,Len),   ?UINT32(Len), X:Len/binary ).
 -define(DEC_INT(I,Len),   ?UINT32(Len), I:Len/big-signed-integer-unit:8 ).
@@ -117,7 +119,6 @@
 
 -define(do_del_opt(C,K,O),  ssh_options:delete_key(C,K,O, ?MODULE,?LINE)).
 -define(DELETE_INTERNAL_OPT(Key,Opts),  ?do_del_opt(internal_options,Key,Opts) ).
--define(DELETE_OPT(KeyOrKeys,Opts),     ?do_del_opt(user_options,KeyOrKeys,Opts)     ).
 
 
 %% Types
@@ -321,6 +322,7 @@
       | tcpip_tunnel_in_daemon_option()
       | authentication_daemon_options()
       | diffie_hellman_group_exchange_daemon_option()
+      | max_initial_idle_time_daemon_option()
       | negotiation_timeout_daemon_option()
       | hello_timeout_daemon_option()
       | hardening_daemon_options()
@@ -361,7 +363,9 @@
       | {user_passwords, [{UserName::string(),Pwd::string()}]}
       | {pk_check_user, boolean()}  
       | {password, string()}
-      | {pwdfun, pwdfun_2() | pwdfun_4()} .
+      | {pwdfun, pwdfun_2() | pwdfun_4()}
+      | {no_auth_needed, boolean()}
+        .
 
 -type prompt_texts() ::
         kb_int_tuple()
@@ -389,6 +393,7 @@
 -type explicit_group_file() :: {file,string()} .
 -type ssh_moduli_file() :: {ssh_moduli_file,string()}.
 
+-type max_initial_idle_time_daemon_option() :: {max_initial_idle_time, timeout()} .
 -type negotiation_timeout_daemon_option() :: {negotiation_timeout, timeout()} .
 -type hello_timeout_daemon_option() :: {hello_timeout, timeout()} .
 
@@ -413,6 +418,11 @@
 
 
 %% Records
+-record(address, {address,
+                  port,
+                  profile
+                 }).
+
 -record(ssh,
 	{
 	  role :: client | role(),
@@ -433,6 +443,8 @@
           send_ext_info, %% May send ext-info to peer
           recv_ext_info, %% Expect ext-info from peer
 
+          kex_strict_negotiated = false,
+
 	  algorithms,   %% #alg{}
 	  
 	  send_mac = none, %% send MAC algorithm
@@ -504,7 +516,8 @@
 	  c_lng,
 	  s_lng,
           send_ext_info,
-          recv_ext_info
+          recv_ext_info,
+          kex_strict_negotiated = false
 	 }).
 
 -record(ssh_pty, {c_version = "", % client version string, e.g "SSH-2.0-Erlang/4.10.5"
@@ -548,5 +561,11 @@
 -define(CIRC_BUF_IN_ONCE(VALUE),
         ((fun(V) -> ?CIRC_BUF_IN(V), V end)(VALUE))
        ).
-                 
+
+-define(SELECT_MSG(__Fun),
+        (fun() ->
+                #{level := __Level} = logger:get_primary_config(),
+                __Fun(__Level)
+        end)()).
+
 -endif. % SSH_HRL defined
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_info.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_info.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_info.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -20,17 +20,17 @@
 
 %%
 %%----------------------------------------------------------------------
-%% Purpose: Print some info of a running ssh aplication.
+%% Purpose: Print some info of a running ssh application.
 %%----------------------------------------------------------------------
 
 -module(ssh_info).
 
 -export([print/0,
 	 print/1,
-	 string/0,
-	 collect_pids/0
+	 string/0
 	]).
 
+-include("ssh.hrl").
 -include("ssh_connect.hrl").
 
 print() ->
@@ -52,14 +52,11 @@ string() ->
 	    [io_lib:nl(),
 	     print_general(),
 	     io_lib:nl(),
-	     underline("Client part", $=),
-             lists:map(fun print_system/1, children(sshc_sup)),
+	     underline("Client(s)", $-),
+	     print_sups(client, sshc_sup),
 	     io_lib:nl(),
-	     underline("Server part", $=),
-             lists:map(fun print_system/1, children(sshd_sup)),
-	     io_lib:nl(),
-	     underline("Supervisors", $=),
-	     walk_sups(ssh_sup),
+	     underline("Daemon(s)", $-),
+	     print_sups(server, sshd_sup),
 	     io_lib:nl()]
     catch
 	_:_ ->
@@ -78,113 +75,226 @@ print_general() ->
      io_lib:format('This printout is generated ~s. ~n',[datetime()])
     ].
 
+print_sups(Role, StartPid) ->
+    walk_tree(Role, get_subs_tree(StartPid)).
+
+%%%================================================================
+get_subs_tree(StartPid) ->
+    lists:foldl(fun({Id,_,worker,_}=C, Acc) -> [{C,chspec(StartPid,Id)}|Acc];
+                   ({Id,Pid,supervisor,_}=C, Acc) -> [{C,chspec(StartPid,Id),get_subs_tree(Pid)}|Acc]
+                end, [], children(StartPid)).
 
-print_system({{server,ssh_system_sup,Addr,Port,Profile}, Pid, supervisor, [ssh_system_sup]}) ->
-    [io_lib:format(?INDENT"Local: ~s (~p children) Profile ~p~n",
-                   [fmt_host_port({Addr,Port}),
-                    ssh_acceptor:number_of_connections(Pid),
-                    Profile
+chspec(Sup, Id) ->
+    try supervisor:get_childspec(Sup, Id)
+    of
+        {ok,Spec} -> Spec;
+        {error,_} -> undefined
+    catch _:_ -> undefined
+    end.
+    
+%%%----------------------------------------------------------------
+walk_tree(Role, Tree) ->
+    walk_tree(Role, Tree, _Indent=?inc(0)).
+ 
+walk_tree(Role, [{{_,_,supervisor,_},_,_}=H|T], Indent) ->
+    [io_lib:format('~s',[format_sup(Role,H,Indent)]) |
+     walk_tree(Role, T, Indent)
+    ];
+walk_tree(Role, [{{_,_,worker,_},_}=H|T], Indent) ->
+    [io_lib:format('~s',[format_wrk(Role,H,Indent)]) |
+     walk_tree(Role, T, Indent)
+    ];
+walk_tree(_Role, [], _) ->
+    "".
+
+
+format_sup(server,
+           {{{ssh_system_sup,LocalAddress},Pid,supervisor,[ssh_system_sup]}, _Spec,
+            [{{{ssh_acceptor_sup,Address},AccSupPid,supervisor,[ssh_acceptor_sup]}, _AccSupSpec,
+              [{{{ssh_acceptor_sup,Address},AccPid,worker,[ssh_acceptor]}, _AccSpec}]}
+             | Children]
+           }, Indent) ->
+   [indent(Indent),
+    io_lib:format("Local listen: ~s, Daemon_ref = ~s~n"
+                  "~ssys_sup=~s, acc_sup=~s, acc=~s~n",
+                  [format_address(LocalAddress),print_pid(Pid),
+                   indent(Indent),print_pid(Pid),print_pid(AccSupPid),print_pid(AccPid)]),
+    walk_tree(server, Children, ?inc(Indent)),
+    io_lib:nl() % Separate system supervisors by an empty line
+   ];
+format_sup(server, {{{ssh_system_sup,LocalAddress},Pid,supervisor,[ssh_system_sup]}, _Spec, Children}, Indent) ->
+    [indent(Indent),
+     io_lib:format("Local listen: none (was: ~s), Daemon_ref = ~s~n"
+                   "~ssys_sup=~s~n",
+                   [format_address(LocalAddress),print_pid(Pid),
+                    indent(Indent),print_pid(Pid)]),
+     walk_tree(server, Children, ?inc(Indent)),
+     io_lib:nl() % Separate system supervisors by an empty line
+    ];
+format_sup(client,
+           {{Ref,ConnSup,supervisor,[ssh_connection_sup]}, _ConnSupSpec,
+            [{{connection,ConnPid,worker,[ssh_connection_handler]}, _ConnSpec}
+             | Children]
+           },
+           Indent) when is_reference(Ref) ->
+    [io_lib:format("~sLocal: ~s~n"
+                   "~sRemote: ~s (Version: ~s)~n"
+                   "~sConnectionRef=~s, connection_sup=~s~n",
+                   [indent(Indent), local_addr(ConnPid),
+                    indent(Indent), peer_addr(ConnPid), peer_version(client,ConnPid),
+                    indent(Indent), print_pid(ConnPid), print_pid(ConnSup)
                    ]),
-     lists:map(fun print_subsystem/1, children(Pid))
+     walk_tree(client,
+               [{H,{connref,ConnPid},Cs} || {H,_,Cs} <- Children],
+               ?inc(Indent)),
+     io_lib:nl() % Separate sub system supervisors by an empty line
     ];
-print_system({{client,ssh_system_sup,Addr,Port,Profile}, Pid, supervisor, [ssh_system_sup]}) ->
-    [io_lib:format(?INDENT"Local: ~s Profile ~p~n",
-                   [fmt_host_port({Addr,Port}),
-                    Profile
+format_sup(server,
+           {{Ref,ConnSup,supervisor,[ssh_connection_sup]}, _ConnSupSpec,
+            [{{connection,ConnPid,worker,[ssh_connection_handler]}, _ConnSpec}
+             | Children]
+           },
+           Indent) when is_reference(Ref) ->
+    [io_lib:format("~sRemote: ~s (Version: ~s)~n"
+                   "~sConnectionRef=~s, connection_sup=~s~n",
+                   [indent(Indent), peer_addr(ConnPid), peer_version(server,ConnPid),
+                    indent(Indent), print_pid(ConnPid), print_pid(ConnSup)
                    ]),
-     lists:map(fun print_subsystem/1, children(Pid))
+     walk_tree(server,
+               [{H,{connref,ConnPid},Cs} || {H,_,Cs} <- Children],
+               ?inc(Indent)),
+     io_lib:nl() % Separate sub system supervisors by an empty line
+    ];
+format_sup(Role, {{channel_sup,Pid,supervisor,[ssh_channel_sup]}, {connref,ConnPid}, Children}, Indent) ->
+    [indent(Indent),
+     case Children of
+         [] ->
+             io_lib:format("No open channels (chan_sup=~s).~n",[print_pid(Pid)]);
+         _ ->
+             Cinfo = try
+                         {ok,L} = ssh_connection_handler:info(ConnPid),
+                         L
+                     catch
+                         _:_ -> []
+                     end,
+             [io_lib:format("Open channels (chan_sup=~s):~n",[print_pid(Pid)]),
+              walk_tree(Role,
+                        [{ChH, lists:keyfind(ChPid,#channel.user,Cinfo)} || {ChH={_,ChPid,_,_},_} <- Children],
+                        ?inc(Indent))
+             ]
+     end
+    ];
+format_sup(Role, {{tcpip_forward_acceptor_sup,Pid,supervisor,[ssh_tcpip_forward_acceptor_sup]}, {connref,_ConnPid}, Children}, Indent) ->
+    [indent(Indent),
+     case Children of
+         [] ->
+             io_lib:format("TCP/IP forwarding not started (fwd_sup=~s)~n", [print_pid(Pid)]);
+         _ ->
+             [io_lib:format("TCP/IP forwarding (fwd_sup=~s):~n", [print_pid(Pid)]),
+              walk_tree(Role, Children, ?inc(Indent))
+             ]
+     end
     ];
-print_system({_, _Pid, worker, [ssh_controller]}) ->
-    ""; % io_lib:format(?INDENT"Controller~n",[]);
-print_system(_X) ->
-    io_lib:format(?INDENT"nyi system ~p~n",[_X]).
+format_sup(Role, {H, Spec, Children}, Indent) ->
+    [indent(Indent),
+     io_lib:format("?: ~200p ~s ~n", [H,print_spec(Spec)]),
+     walk_tree(Role, Children, ?inc(Indent))
+    ].
 
 
+format_wrk(_Role, {{{ssh_acceptor_sup,Address},Pid,worker,[ssh_acceptor]}, _Spec}, Indent) ->
+    [indent(Indent),
+     io_lib:format("acceptor: ~s ~s~n", [format_address(Address),print_pid(Pid)])
+    ];
+format_wrk(_Role, {{{From,To},Pid,worker,[ssh_tcpip_forward_acceptor]}, _Spec}, Indent) ->
+    io_lib:format("~sssh_tcpip_forward_acceptor ~s From: ~s, To: ~s~n", [indent(Indent),print_pid(Pid),
+                                                                         format_address(From), format_address(To)]);
+format_wrk(_Role, {{Ref,Pid,worker,[Cb]}, C}, Indent) when is_reference(Ref) ->
+    Str =
+        try io_lib:format("~p: (remote ~p)~s~s", [C#channel.local_id, C#channel.remote_id,
+                                          if_true(C#channel.sent_close, " sent_close"),
+                                          if_true(C#channel.recv_close, " recv_close")
+                                         ])
+        catch
+            _:_ -> "?:"
+        end,
+    ChCb = try
+               case Cb of
+                   ssh_server_channel -> io_lib:format(" ~s", [Cb:get_print_info(Pid, channel_cb)]);
+                   ssh_client_channel -> io_lib:format(" ~s", [Cb:get_print_info(Pid, channel_cb)]);
+                   _ -> ""
+               end
+           catch _:_ -> ""
+           end,
+    [indent(Indent),
+     io_lib:format("ch ~s ~p~s ~s~n", [Str, Cb, ChCb, print_pid(Pid)])
+    ];
+format_wrk(_Role, {H,Spec}, Indent) ->
+    [indent(Indent),
+     io_lib:format("?: ~200p ~s~n", [H,print_spec(Spec)])
+    ].
 
 
-print_subsystem({{ssh_acceptor_sup,_Addr,_Port,_Profile}, _Pid, supervisor, [ssh_acceptor_sup]}) ->
-    io_lib:format(?INDENT?INDENT"Acceptor~n",[]);
-print_subsystem({Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref),
-                                                               is_pid(Pid) ->
-    Cs = children(Pid),
-    [
-     lists:map(
-       fun(Sup) ->
-                [print_conn(P) || {undefined,P,worker,[ssh_connection_handler]} <- children(Sup)]
-       end,
-       [P ||  {_Ref,P,supervisor,[ssh_connection_sup]} <- Cs]),
-
-     lists:map(
-       fun(Sup) ->
-                [print_ch(M,P) || {_Ref,P,worker,[M]} <- children(Sup),
-                                lists:member(M, [ssh_channel,
-                                                 ssh_channel_sup,
-                                                 ssh_client_channel,
-                                                 ssh_daemon_channel,
-                                                 ssh_server_channel
-                                                ])]
-       end,
-       [P ||  {_Ref,P,supervisor,[ssh_channel_sup]} <- Cs]),
-
-     lists:map(
-       fun(Sup) ->
-               [io_lib:format(?INDENT?INDENT?INDENT"TCP/IP fwd acceptor: ~p~n", [Pa]) 
-                || {undefined,Pa,worker,[ssh_tcpip_forward_acceptor]} <- children(Sup)]
-       end,
-       [P ||  {_Ref,P,supervisor,[ssh_tcpip_forward_acceptor_sup]} <- Cs])
-    ];
 
-print_subsystem(_X) ->
-    io_lib:format(?INDENT?INDENT"nyi subsystem ~p~n",[_X]).
+if_true(true, Str) -> Str;
+if_true(_, _) -> "".
+    
 
+peer_version(Role, Pid) ->
+    try
+        Key =
+            case Role of
+                client -> server_version;
+                server -> client_version
+            end,
+        [{Key, {{_,_},V}}] =
+             ssh_connection_handler:connection_info(Pid, [Key]),
+        V
+    catch
+        _:_ -> "?"
+    end.
 
-print_conn(Pid) ->
+peer_addr(Pid) ->
     try
-	{{_Local,Remote},StrM} = ssh_connection_handler:get_print_info(Pid),
-        io_lib:format(?INDENT?INDENT"Remote: ~s ConnectionRef = ~p ~s~n",[fmt_host_port(Remote),Pid,StrM])
+        [{peer,{_,AddrPort}}] =
+            ssh_connection_handler:connection_info(Pid, [peer]),
+        ssh_lib:format_address_port(AddrPort)
     catch
-      	C:E ->
-	    io_lib:format('****print_conn FAILED for ConnPid ~p: ~p:~p~n',[Pid, C, E])
-    end.  
-        
+        _:_ -> "?"
+    end.
 
-print_ch(CBmod, Pid) ->
+local_addr(Pid) ->
     try
-	{{_ConnManager,ChannelID}, Str} = ssh_server_channel:get_print_info(Pid),
-	io_lib:format(?INDENT?INDENT?INDENT"ch ~p ~p ~p: ~s~n",[ChannelID, Pid, CBmod, Str])
+        [{socket,Socket}] =
+            ssh_connection_handler:connection_info(Pid, [socket]),
+        {ok, AddrPort} = inet:sockname(Socket),
+        ssh_lib:format_address_port(AddrPort)
     catch
-	C:E ->
-	    io_lib:format('****print_ch FAILED for ChanPid ~p: ~p:~p~n',[Pid, C, E])
+        _:_ -> "?"
     end.
 
-%%%================================================================
-walk_sups(StartPid) ->
-    walk_sups(children(StartPid), _Indent=?inc(0)).
+format_address(#address{address=Addr, port=Port, profile=Prof}) ->
+    io_lib:format("~s (profile ~p)", [ssh_lib:format_address_port({Addr,Port}),Prof]);
+format_address(A) ->
+    io_lib:format("~p",[A]).
 
-walk_sups([H={_,Pid,_,_}|T], Indent) ->
-    [indent(Indent),
-     io_lib:format('~200p  ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]),
-     case H of
-	 {_,_,supervisor,[ssh_connection_handler]} -> "";
-	 {_,Pid,supervisor,_} -> walk_sups(children(Pid), ?inc(Indent));
-	 _ -> ""
-     end,
-     walk_sups(T, Indent)
-    ];
-walk_sups([], _) ->
-    "".
+
+print_pid(Pid) ->
+    io_lib:format("~p~s",[Pid, dead_or_alive(Pid)]).
 
 dead_or_alive(Name) when is_atom(Name) ->
     case whereis(Name) of
 	undefined ->
-	    "**UNDEFINED**";
+	    " **UNDEFINED**";
 	Pid ->
 	    dead_or_alive(Pid)
     end;
 dead_or_alive(Pid) when is_pid(Pid) ->
-    case process_info(Pid) of
-	undefined -> "**DEAD**";
-	_ -> "alive"
+    case process_info(Pid,message_queue_len) of
+	undefined -> " ***DEAD***";
+        {message_queue_len,N} when N>10 -> io_lib:format(" ***msg_queue_len: ~p***",[N]);
+        {message_queue_len,N} when N>0 -> io_lib:format(" (msg_queue_len: ~p)",[N]);
+	_ -> ""
     end.
 
 indent(I) -> io_lib:format('~*c',[I,$ ]).
@@ -204,29 +314,8 @@ children(Pid) ->
 	    []
     end.
 
-is_connection_handler(Pid) ->
-    try
-	{ssh_connection_handler,init,_} =
-	    proplists:get_value(
-	      '$initial_call',
-	      proplists:get_value(
-		dictionary,
-		process_info(Pid, [dictionary])))
-    of
-	_ -> true
-
-    catch
-	_:_ ->
-	    false
-    end.
-
-channels(Pid) ->
-    case is_connection_handler(Pid) of
-	true ->
-	    ssh_connection_handler:info(Pid,all);
-	false ->
-	    false
-    end.
+%%%================================================================
+print_spec(_Spec) -> "".
 
 %%%================================================================
 underline(Str, LineChar) ->
@@ -238,85 +327,7 @@ datetime() ->
     lists:flatten(io_lib:format('~4w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w UTC',[YYYY,MM,DD, H,M,S])).
 
 
-fmt_host_port({{A,B,C,D},Port}) -> io_lib:format('~p.~p.~p.~p:~p',[A,B,C,D,Port]);
-fmt_host_port({Host,Port}) -> io_lib:format('~s:~p',[Host,Port]).
-
-%%%################################################################
-collect_pids() -> collect_pids(ssh_sup).
-
-collect_pids(P) ->
-    Collector = pcollect_pids(P, spawn(fun init_collector/0)),
-    Collector ! {get_values,self()},
-    receive
-	{values,Values} ->
-	    Values
-    end.
-
-%%%----------------
-pcollect_pids(undefined, Collector) ->
-    Collector;
-
-pcollect_pids(A, Collector) when is_atom(A) ->
-    pcollect_pids(whereis(A), Collector);
-
-pcollect_pids(Pid, Collector) when is_pid(Pid) ->
-    Collector ! {expect,Pid},
-    spawn(fun() ->
-		  lists:foreach(
-		    fun(P2) ->
-			    pcollect_pids(P2,Collector)
-		    end, children(Pid)),
-		  Collector ! {value,Pid,Pid}
-	  end),
-    Collector;
-
-pcollect_pids({Ref,Pid,supervisor,_}, Collector) when is_pid(Pid),
-						      is_reference(Ref) ->
-    pcollect_pids(Pid, Collector);
-
-pcollect_pids({sshc_sup,Pid,supervisor,_}, Collector) when is_pid(Pid) ->
-    pcollect_pids(Pid, Collector);
-
-pcollect_pids({sshd_sup,Pid,supervisor,_}, Collector) when is_pid(Pid) ->
-    pcollect_pids(Pid, Collector);
-
-pcollect_pids({{ssh_acceptor_sup,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) ->
-    pcollect_pids(Pid, Collector);
-
-pcollect_pids({{server,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) ->
-    pcollect_pids(Pid, Collector);
-
-pcollect_pids({{server,_,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) ->
-    pcollect_pids(Pid, Collector);
-
-pcollect_pids({undefined,Pid,supervisor,[ssh_connection_handler]}, Collector) ->
-    Collector ! {value,Pid,Pid},
-    case channels(Pid) of
-	{ok,L} ->
-	    [Collector!{value,P,P} || #channel{user=P} <- L];
-	_ ->
-	    ok
-    end,
-    Collector;
-
-pcollect_pids({_,Pid,_,_}, Collector) when is_pid(Pid) ->
-    Collector ! {value,Pid,Pid},
-    Collector;
-
-pcollect_pids(_, Collector) ->
-    Collector.
-
-%%%----------------
-init_collector() ->
-    loop_collector([],[]).
+%%%================================================================
 
-loop_collector(Expects, Values) ->
-    receive
-	{expect, Ref} ->
-	    loop_collector([Ref|Expects], Values);
-	{value, Ref, Val} ->
-	    loop_collector(Expects--[Ref], [Val|Values]);
-	{get_values, From} when Expects==[] ->
-%%				Values=/=[] ->
-	    From ! {values,Values}
-    end.
+%% ?wr_record(address);
+%% wr_record(R) -> io_lib:format('~p~n',[R]).
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_message.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_message.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_message.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2013-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 -module(ssh_message).
 
 -include_lib("public_key/include/public_key.hrl").
+-include_lib("kernel/include/logger.hrl").
 
 -include("ssh.hrl").
 -include("ssh_connect.hrl").
@@ -33,16 +34,21 @@
 -export([encode/1, decode/1, decode_keyboard_interactive_prompts/2]).
 -export([ssh2_pubkey_decode/1,
          ssh2_pubkey_encode/1,
-         ssh2_privkey_decode2/1]).
+         ssh2_privkey_decode2/1,
+         oid2ssh_curvename/1,
+         ssh_curvename2oid/1,
+         %% experimental:
+         ssh2_privkey_encode/1
+        ]).
 
 -behaviour(ssh_dbg).
 -export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
-
+-define(ALG_NAME_LIMIT, 64). % RFC4251 sec6
 
 ucl(B) ->
     try unicode:characters_to_list(B) of
 	L when is_list(L) -> L;
-	{error,_Matched,Rest} -> throw({error,{bad_unicode,Rest}})
+	{error,_Matched,_Rest} -> throw({error,bad_unicode})
     catch
 	_:_ -> throw({error,bad_unicode})
     end.
@@ -170,7 +176,7 @@ encode(#ssh_msg_userauth_pk_ok{
     <<?Ebyte(?SSH_MSG_USERAUTH_PK_OK), ?Estring(Alg), ?Ebinary(KeyBlob)>>;
 
 encode(#ssh_msg_userauth_passwd_changereq{prompt = Prompt,
-					  languge = Lang
+					  language = Lang
 					 })->
     <<?Ebyte(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?Estring_utf8(Prompt), ?Estring(Lang)>>;
 
@@ -202,12 +208,12 @@ encode(#ssh_msg_disconnect{
 encode(#ssh_msg_service_request{
 	  name = Service
 	 }) ->
-    <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring_utf8(Service)>>;
+    <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring(Service)>>;
 
 encode(#ssh_msg_service_accept{
 	  name = Service
 	 }) ->
-    <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring_utf8(Service)>>;
+    <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring(Service)>>;
 
 encode(#ssh_msg_ext_info{
           nr_extensions = N,
@@ -370,7 +376,7 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_REQUEST)
     try
         #ssh_msg_channel_request{
            recipient_channel = Recipient,
-           request_type = ?unicode_list(RequestType),
+           request_type = binary:bin_to_list(RequestType),
            want_reply = erl_boolean(Bool),
            data  = Data
           }
@@ -400,8 +406,8 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_REQUEST
 	 Data/binary>>) ->
     #ssh_msg_userauth_request{
        user =    ?unicode_list(User),
-       service = ?unicode_list(Service),
-       method =  ?unicode_list(Method),
+       service = binary:bin_to_list(Service),
+       method =  binary:bin_to_list(Method),
        data = Data
       };
 
@@ -409,7 +415,7 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_FAILURE
 	 ?DEC_BIN(Auths,__0),
 	 ?BYTE(Bool)>>) ->
     #ssh_msg_userauth_failure {
-       authentications = ?unicode_list(Auths),
+       authentications = binary:bin_to_list(Auths),
        partial_success = erl_boolean(Bool)
       };
 
@@ -436,7 +442,7 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RE
 decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?DEC_BIN(Prompt,__0), ?DEC_BIN(Lang,__1) >>) ->
     #ssh_msg_userauth_passwd_changereq{
        prompt = Prompt,
-       languge = Lang
+       language = Lang
       };
 
 %%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
@@ -523,12 +529,12 @@ decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_
 
 decode(<<?SSH_MSG_SERVICE_REQUEST, ?DEC_BIN(Service,__0)>>) ->
     #ssh_msg_service_request{
-       name = ?unicode_list(Service)
+       name = binary:bin_to_list(Service)
       };
 
 decode(<<?SSH_MSG_SERVICE_ACCEPT, ?DEC_BIN(Service,__0)>>) ->
     #ssh_msg_service_accept{
-       name = ?unicode_list(Service)
+       name = binary:bin_to_list(Service)
       };
 
 decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1)>>) ->
@@ -569,16 +575,29 @@ decode(<<?BYTE(?SSH_MSG_DEBUG), ?BYTE(Bo
 %%%-------- public key --------
 ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
     <<?STRING(<<"ssh-rsa">>), ?Empint(E), ?Empint(N)>>;
+
 ssh2_pubkey_encode({Y,  #'Dss-Parms'{p = P, q = Q, g = G}}) ->
     <<?STRING(<<"ssh-dss">>), ?Empint(P), ?Empint(Q), ?Empint(G), ?Empint(Y)>>;
+
+ssh2_pubkey_encode({#'ECPoint'{point = Q}, {namedCurve,OID}}) when OID == ?'id-Ed25519' orelse
+                                                                   OID == ?'id-Ed448' ->
+    {KeyType, _} = oid2ssh_curvename(OID),
+    <<?STRING(KeyType), ?Estring(Q)>>;
+
+ssh2_pubkey_encode(#'ECPrivateKey'{parameters = {namedCurve,OID},
+                                   publicKey = Key}) when OID == ?'id-Ed25519' orelse
+                                                          OID == ?'id-Ed448' ->
+    {KeyType, _} = oid2ssh_curvename(OID),
+    <<?STRING(KeyType), ?Estring(Key)>>;
+
+ssh2_pubkey_encode(#'ECPrivateKey'{parameters = {namedCurve,OID},
+                                   publicKey = Key}) ->
+    {KeyType,Curve} = oid2ssh_curvename(OID),
+    <<?STRING(KeyType), ?STRING(Curve), ?Estring(Key)>>;
+
 ssh2_pubkey_encode({#'ECPoint'{point = Q}, {namedCurve,OID}}) ->
-    Curve = public_key:oid2ssh_curvename(OID),
-    KeyType = <<"ecdsa-sha2-", Curve/binary>>,
-    <<?STRING(KeyType), ?STRING(Curve), ?Estring(Q)>>;
-ssh2_pubkey_encode({ed_pub, ed25519, Key}) ->
-    <<?STRING(<<"ssh-ed25519">>), ?Estring(Key)>>;
-ssh2_pubkey_encode({ed_pub, ed448, Key}) ->
-    <<?STRING(<<"ssh-ed448">>), ?Estring(Key)>>.
+    {KeyType,Curve} = oid2ssh_curvename(OID),
+    <<?STRING(KeyType), ?STRING(Curve), ?Estring(Q)>>.
 
 %%%--------
 ssh2_pubkey_decode(KeyBlob) ->
@@ -602,33 +621,90 @@ ssh2_pubkey_decode2(<<?UINT32(7), "ssh-d
                       q = Q,
                       g = G}
      }, Rest};
-ssh2_pubkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) ->
-    Sz = TL-11,
-    <<_Curve:Sz/binary,
-      ?DEC_BIN(SshName, _IL),
-      ?DEC_BIN(Q, _QL),
-      Rest/binary>> = KeyRest,
-    OID = public_key:ssh_curvename2oid(SshName),
-    {{#'ECPoint'{point = Q}, {namedCurve,OID}
-     }, Rest};
-ssh2_pubkey_decode2(<<?UINT32(11), "ssh-ed25519",
-                      ?DEC_BIN(Key, _L),
-                      Rest/binary>>) ->
-    {{ed_pub, ed25519, Key},
-     Rest};
-ssh2_pubkey_decode2(<<?UINT32(9), "ssh-ed448",
-                      ?DEC_BIN(Key, _L),
-                      Rest/binary>>) ->
-    {{ed_pub, ed448, Key},
+
+ssh2_pubkey_decode2(<<?DEC_BIN(SshCurveName,SCNL), Rest0/binary>>) ->
+    {Pub, Rest} =
+        case {SshCurveName, Rest0} of
+            {<<"ecdsa-sha2-", _/binary>>,
+             <<?DEC_BIN(_Curve, _IL),
+               ?DEC_BIN(Q, _QL),
+               Rest1/binary>>} ->  {Q, Rest1};
+            
+            {<<"ssh-ed",_/binary>>,
+             <<?DEC_BIN(Key, _L),
+               Rest1/binary>>} ->  {Key, Rest1}
+        end,
+    OID = ssh_curvename2oid(SshCurveName),
+    {{#'ECPoint'{point = Pub}, {namedCurve,OID}},
      Rest}.
-                     
+
 %%%-------- private key --------
 
 %% dialyser... ssh2_privkey_decode(KeyBlob) ->
 %% dialyser...     {Key,_RestBlob} = ssh2_privkey_decode2(KeyBlob),
 %% dialyser...     Key.
-
 %% See sshkey_private_serialize_opt in sshkey.c
+
+ssh2_privkey_encode(#'RSAPrivateKey'
+                    {version = 'two-prime', % Found this in public_key:generate_key/1 ..
+                     modulus = N,
+                     publicExponent = E,
+                     privateExponent = D,
+                     prime1 = P,
+                     prime2 = Q,
+                     %% exponent1, % D_mod_P_1
+                     %% exponent2, % D_mod_Q_1
+                     coefficient = IQMP
+                    }) ->
+    <<?STRING(<<"ssh-rsa">>),
+      ?Empint(N), % Yes, N and E is reversed relative pubkey format
+      ?Empint(E), % --"--
+      ?Empint(D),
+      ?Empint(IQMP),
+      ?Empint(P),
+      ?Empint(Q)>>;
+
+ssh2_privkey_encode(#'DSAPrivateKey'
+                    {version = 0,
+                     p = P,
+                     q = Q,
+                     g = G,
+                     y = Y,
+                     x = X
+                    }) ->
+    <<?STRING(<<"ssh-dss">>),
+      ?Empint(P),
+      ?Empint(Q),
+      ?Empint(G),
+      ?Empint(Y), % Publ key
+      ?Empint(X)  % Priv key
+    >>;
+
+ssh2_privkey_encode(#'ECPrivateKey'
+                    {version = 1,
+                     parameters = {namedCurve,OID},
+                     privateKey = Priv,
+                     publicKey = Pub
+                    }) when OID == ?'id-Ed25519' orelse
+                            OID == ?'id-Ed448' ->
+    {CurveName,_} = oid2ssh_curvename(OID),
+    <<?STRING(CurveName),
+      ?STRING(Pub),
+      ?STRING(Priv)>>;
+
+ssh2_privkey_encode(#'ECPrivateKey'
+                    {version = 1,
+                     parameters = {namedCurve,OID},
+                     privateKey = Priv,
+                     publicKey = Q
+                    }) ->
+    {CurveName,_} = oid2ssh_curvename(OID),
+    <<?STRING(CurveName),
+      ?STRING(CurveName), % SIC!
+      ?STRING(Q),
+      ?STRING(Priv)>>.
+      
+%%%--------
 ssh2_privkey_decode2(<<?UINT32(7), "ssh-rsa",
                        ?DEC_INT(N, _NL), % Yes, N and E is reversed relative pubkey format
                        ?DEC_INT(E, _EL), % --"--
@@ -661,30 +737,49 @@ ssh2_privkey_decode2(<<?UINT32(7), "ssh-
                       y = Y,
                       x = X
                      }, Rest};
-ssh2_privkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) ->
-    Sz = TL-11,
-    <<_Curve:Sz/binary,
-      ?DEC_BIN(CurveName, _SNN),
-      ?DEC_BIN(Q, _QL),
-      ?DEC_BIN(Priv, _PrivL),
-      Rest/binary>> = KeyRest,
-    OID = public_key:ssh_curvename2oid(CurveName),
+
+ssh2_privkey_decode2(<<?DEC_BIN(SshCurveName,SCNL), Rest0/binary>>) ->
+    {Pub, Priv, Rest} =
+        case {SshCurveName, Rest0} of
+            {<<"ecdsa-sha2-",_/binary>>,
+             <<?DEC_BIN(_Curve, _IL),
+               ?DEC_BIN(Pub1, _QL),
+               ?DEC_BIN(Priv1, _PrivL),
+               Rest1/binary>>} ->
+                {Pub1, Priv1, Rest1};
+
+            {<<"ssh-ed",_/binary>>,
+             <<?DEC_BIN(Pub1, PL),
+               ?DEC_BIN(PrivPub, PPL),
+               Rest1/binary>>} ->
+               PL = PPL div 2,
+                <<Priv1:PL/binary, _/binary>> = PrivPub,
+                {Pub1, Priv1, Rest1}
+        end,
+    OID = ssh_curvename2oid(SshCurveName),
     {#'ECPrivateKey'{version = 1,
                      parameters = {namedCurve,OID},
                      privateKey = Priv,
-                     publicKey = Q
-                    }, Rest};
-ssh2_privkey_decode2(<<?UINT32(11), "ssh-ed25519",
-                       ?DEC_BIN(Pub,_Lpub),
-                       ?DEC_BIN(Priv,_Lpriv),
-                       Rest/binary>>) ->
-    {{ed_pri, ed25519, Pub, Priv}, Rest};
-ssh2_privkey_decode2(<<?UINT32(9), "ssh-ed448",
-                       ?DEC_BIN(Pub,_Lpub),
-                       ?DEC_BIN(Priv,_Lpriv),
-                       Rest/binary>>) ->
-    {{ed_pri, ed448, Pub, Priv}, Rest}.
+                     publicKey = Pub
+                    }, Rest}.
+
 
+%% Description: Converts from the ssh name of elliptic curves to
+%% the OIDs.
+%%--------------------------------------------------------------------
+ssh_curvename2oid(<<"ssh-ed25519">>) -> ?'id-Ed25519';
+ssh_curvename2oid(<<"ssh-ed448">>  ) -> ?'id-Ed448';
+ssh_curvename2oid(<<"ecdsa-sha2-nistp256">>) -> ?'secp256r1';
+ssh_curvename2oid(<<"ecdsa-sha2-nistp384">>) -> ?'secp384r1';
+ssh_curvename2oid(<<"ecdsa-sha2-nistp521">>) -> ?'secp521r1'.
+
+%% Description: Converts from elliptic curve OIDs to the ssh name.
+%%--------------------------------------------------------------------
+oid2ssh_curvename(?'id-Ed25519')-> {<<"ssh-ed25519">>, 'n/a'};
+oid2ssh_curvename(?'id-Ed448')  -> {<<"ssh-ed448">>,   'n/a'};
+oid2ssh_curvename(?'secp256r1') -> {<<"ecdsa-sha2-nistp256">>, <<"nistp256">>};
+oid2ssh_curvename(?'secp384r1') -> {<<"ecdsa-sha2-nistp384">>, <<"nistp384">>};
+oid2ssh_curvename(?'secp521r1') -> {<<"ecdsa-sha2-nistp521">>, <<"nistp521">>}.
 
 %%%================================================================
 %%%
@@ -726,9 +821,33 @@ decode_kex_init(<<?BYTE(Bool)>>, Acc, 0)
     %% See rfc 4253 7.1
     X = 0,
     list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
-decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) ->
-    Names = string:tokens(?unicode_list(Data), ","),
-    decode_kex_init(Rest, [Names | Acc], N -1).
+decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) when
+      byte_size(Data) < ?MAX_NUM_ALGORITHMS * ?ALG_NAME_LIMIT ->
+    BinParts = binary:split(Data, <<$,>>, [global]),
+    AlgCount = length(BinParts),
+    case AlgCount =< ?MAX_NUM_ALGORITHMS of
+        true ->
+            Process =
+                fun(<<>>, PAcc) ->
+                        PAcc;
+                   (Part, PAcc) ->
+                        case byte_size(Part) =< ?ALG_NAME_LIMIT of
+                            true ->
+                                Name = binary:bin_to_list(Part),
+                                [Name | PAcc];
+                            false ->
+                                ?LOG_DEBUG("Ignoring too long name", []),
+                                PAcc
+                        end
+                end,
+            Names = lists:foldr(Process, [], BinParts),
+            decode_kex_init(Rest, [Names | Acc], N - 1);
+        false ->
+            throw({error, {kexinit_error, N, {alg_count, AlgCount}}})
+    end;
+decode_kex_init(<<?DEC_BIN(Data,__0), _Rest/binary>>, _Acc, N) ->
+    throw({error, {kexinit, N, {string_size, byte_size(Data)}}}).
+
 
 
 %%%================================================================
@@ -746,13 +865,8 @@ encode_signature(#'RSAPublicKey'{}, SigA
 encode_signature({_, #'Dss-Parms'{}}, _SigAlg, Signature) ->
     <<?Ebinary(<<"ssh-dss">>), ?Ebinary(Signature)>>;
 encode_signature({#'ECPoint'{}, {namedCurve,OID}}, _SigAlg, Signature) ->
-    Curve = public_key:oid2ssh_curvename(OID),
-    <<?Ebinary(<<"ecdsa-sha2-",Curve/binary>>), ?Ebinary(Signature)>>;
-encode_signature({ed_pub, ed25519,_}, _SigAlg, Signature) ->
-    <<?Ebinary(<<"ssh-ed25519">>), ?Ebinary(Signature)>>;
-encode_signature({ed_pub, ed448,_}, _SigAlg, Signature) ->
-    <<?Ebinary(<<"ssh-ed448">>), ?Ebinary(Signature)>>.
-    
+    {SshCurveName,_} = oid2ssh_curvename(OID),
+    <<?Ebinary(<<SshCurveName/binary>>), ?Ebinary(Signature)>>.
 
 
 %%%################################################################
@@ -878,4 +992,3 @@ ssh_dbg_format(raw_messages, {return_fro
 ?wr_record(ssh_msg_channel_failure);
 
 wr_record(R) -> io_lib:format('~p~n',[R]).
-
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_options.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_options.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_options.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -34,7 +34,8 @@
          keep_set_options/2,
          no_sensitive/2,
          initial_default_algorithms/2,
-         check_preferred_algorithms/1
+         check_preferred_algorithms/1,
+         merge_options/3
         ]).
 
 -export_type([private_options/0
@@ -146,26 +147,18 @@ put_socket_value(A, SockOpts) when is_at
 -spec delete_key(option_class(), option_key(), private_options(),
                  atom(), non_neg_integer()) -> private_options().
 
-delete_key(user_options, Key, Opts, _CallerMod, _CallerLine) when is_map(Opts) ->
-   if
-       is_list(Key) ->
-           lists:foldl(fun maps:remove/2, Opts, Key);
-       true ->
-           maps:remove(Key, Opts)
-   end;
-
-delete_key(Class, Key, Opts, _CallerMod, _CallerLine) when is_map(Opts) andalso
-                                                           (Class == socket_options orelse
-                                                            Class == internal_options) ->
-    Opts#{Class := 
-              if
-                  is_list(Key) ->
-                      lists:foldl(fun maps:remove/2, maps:get(Class,Opts), Key);
-                  true ->
-                      maps:remove(Key, maps:get(Class,Opts))
-              end
-         }.
+delete_key(internal_options, Key, Opts, _CallerMod, _CallerLine) when is_map(Opts) ->
+    InternalOpts = maps:get(internal_options,Opts),
+    Opts#{internal_options := maps:remove(Key, InternalOpts)}.
+        
 
+%%%================================================================
+%%%
+%%% Replace 0 or more options in an options map
+%%%
+merge_options(Role, NewPropList, Opts0) when is_list(NewPropList),
+                                             is_map(Opts0) ->
+    check_and_save(NewPropList, default(Role), Opts0).
 
 %%%================================================================
 %%%
@@ -183,8 +176,7 @@ handle_options(Role, PropList0) ->
 handle_options(Role, OptsList0, Opts0) when is_map(Opts0),
                          is_list(OptsList0) ->
     OptsList1 = proplists:unfold(
-                  lists:foldr(fun(T,Acc) when is_tuple(T),
-                                              size(T) =/= 2-> [{special_trpt_args,T} | Acc];
+                  lists:foldr(fun(T,Acc) when tuple_size(T) =/= 2 -> [{special_trpt_args,T} | Acc];
                                  (X,Acc) -> [X|Acc]
                               end,
                               [], OptsList0)),
@@ -235,10 +227,8 @@ handle_options(Role, OptsList0, Opts0) w
 
         %% Enter the user's values into the map; unknown keys are
         %% treated as socket options
-        final_preferred_algorithms(
-          lists:foldl(fun(KV, Vals) ->
-                              save(KV, OptionDefinitions, Vals)
-                      end, InitialMap, OptsList2))
+        check_and_save(OptsList2, OptionDefinitions, InitialMap)
+
     catch
         error:{EO, KV, Reason} when EO == eoptions ; EO == eerl_env ->
             if
@@ -251,6 +241,13 @@ handle_options(Role, OptsList0, Opts0) w
             end
     end.
 
+check_and_save(OptsList, OptionDefinitions, InitialMap) ->
+    final_preferred_algorithms(
+      lists:foldl(fun(KV, Vals) ->
+                          save(KV, OptionDefinitions, Vals)
+                  end, InitialMap, OptsList)).
+    
+
 cnf_key(server) -> server_options;
 cnf_key(client) -> client_options.
 
@@ -311,7 +308,7 @@ save({Inet,false}, _Defs, OptMap) when I
 save({special_trpt_args,T}, _Defs, OptMap) when is_map(OptMap) ->
     OptMap#{socket_options := [T | maps:get(socket_options,OptMap)]};
 
-%% and finaly the 'real stuff':
+%% and finally the 'real stuff':
 save({Key,Value}, Defs, OptMap) when is_map(OptMap) ->
     try (check_fun(Key,Defs))(Value)
     of
@@ -493,6 +490,12 @@ default(server) ->
             class => user_option
            },
 
+      no_auth_needed =>
+          #{default => false,
+            chk => fun(V) -> erlang:is_boolean(V) end,
+            class => user_option
+           },
+
       pk_check_user =>
           #{default => false,
             chk => fun(V) -> erlang:is_boolean(V) end,
@@ -529,6 +532,12 @@ default(server) ->
             class => user_option
            },
 
+      max_initial_idle_time =>
+          #{default => infinity, %% To not break compatibility
+            chk => fun(V) -> check_timeout(V) end,
+            class => user_option
+           },
+
       negotiation_timeout =>
           #{default => 2*60*1000,
             chk => fun(V) -> check_timeout(V) end,
@@ -876,6 +885,12 @@ default(common) ->
            #{default => ?MAX_RND_PADDING_LEN,
              chk => fun(V) -> check_non_neg_integer(V) end,
              class => undoc_user_option
+            },
+
+       channel_close_timeout =>
+           #{default => 5 * 1000,
+             chk => fun(V) -> check_non_neg_integer(V) end,
+             class => undoc_user_option
             }
      }.
 
@@ -1069,7 +1084,7 @@ check_modify_algorithms(M) when is_list(
     [error_in_check(Op_KVs, "Bad modify_algorithms")
      || Op_KVs <- M,
         not is_tuple(Op_KVs)
-            orelse (size(Op_KVs) =/= 2)
+            orelse (tuple_size(Op_KVs) =/= 2)
             orelse (not lists:member(element(1,Op_KVs), [append,prepend,rm]))],
     {true, [{Op,normalize_mod_algs(KVs,false)} || {Op,KVs} <- M]};
 check_modify_algorithms(_) ->
@@ -1094,11 +1109,11 @@ normalize_mod_algs([K|Ks], KVs0, Acc, Us
     normalize_mod_algs(Ks, KVs, [{K,Vs} | Acc], UseDefaultAlgs);
 normalize_mod_algs([], [], Acc, _) ->
     %% No values left in the key-value list after removing the expected entries
-    %% (thats good)
+    %% (that's good)
     lists:reverse(Acc);
 normalize_mod_algs([], [{K,_}|_], _, _) ->
     %% Some values left in the key-value list after removing the expected entries
-    %% (thats bad)
+    %% (that's bad)
     case ssh_transport:algo_class(K) of
         true -> error_in_check(K, "Duplicate key");
         false -> error_in_check(K, "Unknown key")
@@ -1181,7 +1196,7 @@ check_input_ok(Algs) ->
     [error_in_check(KVs, "Bad preferred_algorithms")
      || KVs <- Algs,
         not is_tuple(KVs)
-            orelse (size(KVs) =/= 2)].
+            orelse (tuple_size(KVs) =/= 2)].
 
 %%%----------------------------------------------------------------
 final_preferred_algorithms(Options0) ->
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_sftpd.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_sftpd.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_sftpd.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
 -behaviour(ssh_server_channel).
 
 -include_lib("kernel/include/file.hrl").
-
+-include_lib("kernel/include/logger.hrl").
 -include("ssh.hrl").
 -include("ssh_xfer.hrl").
 -include("ssh_connect.hrl"). %% For ?DEFAULT_PACKET_SIZE and ?DEFAULT_WINDOW_SIZE
@@ -52,6 +52,8 @@
 	  file_handler,			% atom() - callback module 
 	  file_state,                   % state for the file callback module
 	  max_files,                    % integer >= 0 max no files sent during READDIR
+	  max_handles,                  % integer > 0  - max number of file handles
+	  max_path,                     % integer > 0 - max length of path
 	  options,			% from the subsystem declaration
 	  handles			% list of open handles
 	  %% handle is either {<int>, directory, {Path, unread|eof}} or
@@ -65,6 +67,8 @@
       Options :: [ {cwd, string()} |
                    {file_handler, CbMod | {CbMod, FileState}} |
                    {max_files, integer()} |
+                   {max_handles, integer()} |
+                   {max_path, integer()} |
                    {root, string()} |
                    {sftpd_vsn, integer()}
                  ],
@@ -102,7 +106,7 @@ init(Options) ->
     
     %% Get the root of the file system (symlinks must be followed,
     %% otherwise the realpath call won't work). But since symbolic links
-    %% isn't supported on all plattforms we have to use the root property
+    %% isn't supported on all platforms we have to use the root property
     %% supplied by the user.
     {Root, State} = 
 	case resolve_symlinks(Root0, 
@@ -115,8 +119,14 @@ init(Options) ->
 		{Root0, State0}
 	end,
     MaxLength = proplists:get_value(max_files, Options, 0),
+    MaxHandles = proplists:get_value(max_handles, Options, 1000),
+    MaxPath = proplists:get_value(max_path, Options, 4096),
     Vsn = proplists:get_value(sftpd_vsn, Options, 5),
-    {ok,  State#state{cwd = CWD, root = Root, max_files = MaxLength,
+    {ok,  State#state{cwd = CWD,
+                      root = Root,
+                      max_files = MaxLength,
+                      max_handles = MaxHandles,
+                      max_path = MaxPath,
 		      options = Options,
 		      handles = [], pending = <<>>,
 		      xf = #ssh_xfer{vsn = Vsn, ext = []}}}.
@@ -128,9 +138,8 @@ init(Options) ->
 %% Description: Handles channel messages
 %%--------------------------------------------------------------------
 handle_ssh_msg({ssh_cm, _ConnectionManager,
-		{data, _ChannelId, Type, Data}}, State) ->
-    State1 = handle_data(Type, Data, State),
-    {ok, State1};
+		{data, ChannelId, Type, Data}}, State) ->
+    handle_data(Type, ChannelId, Data, State);
 
 handle_ssh_msg({ssh_cm, _, {eof, ChannelId}}, State) ->
     {stop, ChannelId, State};
@@ -187,24 +196,77 @@ terminate(_, #state{handles=Handles, fil
 %%--------------------------------------------------------------------
 %%% Internal functions
 %%--------------------------------------------------------------------
-handle_data(0, <<?UINT32(Len), Msg:Len/binary, Rest/binary>>, 
+handle_data(0, ChannelId, <<?UINT32(Len), Msg:Len/binary, Rest/binary>>,
 	    State = #state{pending = <<>>}) ->
     <<Op, ?UINT32(ReqId), Data/binary>> = Msg,
     NewState = handle_op(Op, ReqId, Data, State),
     case Rest of
 	<<>> ->
-	    NewState;   
+	    {ok, NewState};
 	_ ->
-	    handle_data(0, Rest, NewState)
+	    handle_data(0, ChannelId, Rest, NewState)
     end;
-	     
-handle_data(0, Data, State = #state{pending = <<>>}) ->
-    State#state{pending = Data};
-
-handle_data(Type, Data, State = #state{pending = Pending}) -> 
-     handle_data(Type, <<Pending/binary, Data/binary>>, 
-                 State#state{pending = <<>>}).
- 
+handle_data(0, _ChannelId, Data, State = #state{pending = <<>>}) ->
+    {ok, State#state{pending = Data}};
+handle_data(Type, ChannelId, Data0, State = #state{pending = Pending}) ->
+    Data = <<Pending/binary, Data0/binary>>,
+    Size = byte_size(Data),
+    case Size > ?SSH_MAX_PACKET_SIZE of
+        true ->
+            ReportFun =
+                fun([S]) ->
+                        Report =
+                            #{label => {error_logger, error_report},
+                              report =>
+                                  io_lib:format("SFTP packet size (~B) exceeds the limit!",
+                                                [S])},
+                        Meta =
+                            #{error_logger =>
+                                  #{tag => error_report,type => std_error},
+                             report_cb => fun(#{report := Msg}) -> {Msg, []} end},
+                        {Report, Meta}
+                end,
+            ?LOG_ERROR(ReportFun, [Size]),
+            {stop, ChannelId, State};
+        _ ->
+            handle_data(Type, ChannelId, Data, State#state{pending = <<>>})
+    end.
+
+%% From draft-ietf-secsh-filexfer-02 "The file handle strings MUST NOT be longer than 256 bytes."
+handle_op(Request, ReqId, <<?UINT32(HLen), _/binary>>, State = #state{xf = XF})
+  when (Request == ?SSH_FXP_CLOSE orelse
+        Request == ?SSH_FXP_FSETSTAT orelse
+        Request == ?SSH_FXP_FSTAT orelse
+        Request == ?SSH_FXP_READ orelse
+        Request == ?SSH_FXP_READDIR orelse
+        Request == ?SSH_FXP_WRITE),
+       HLen > 256 ->
+    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_INVALID_HANDLE, "Invalid handle"),
+    State;
+handle_op(Request, ReqId, <<?UINT32(PLen), _/binary>>,
+          State = #state{max_path = MaxPath, xf = XF})
+  when (Request == ?SSH_FXP_LSTAT orelse
+        Request == ?SSH_FXP_MKDIR orelse
+        Request == ?SSH_FXP_OPEN orelse
+        Request == ?SSH_FXP_OPENDIR orelse
+        Request == ?SSH_FXP_READLINK orelse
+        Request == ?SSH_FXP_REALPATH orelse
+        Request == ?SSH_FXP_REMOVE orelse
+        Request == ?SSH_FXP_RMDIR orelse
+        Request == ?SSH_FXP_SETSTAT orelse
+        Request == ?SSH_FXP_STAT),
+       PLen > MaxPath ->
+    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NO_SUCH_PATH,
+                            "No such path"),
+    State;
+handle_op(Request, ReqId, <<?UINT32(PLen), _:PLen/binary, ?UINT32(PLen2), _/binary>>,
+          State = #state{max_path = MaxPath, xf = XF})
+  when (Request == ?SSH_FXP_RENAME orelse
+        Request == ?SSH_FXP_SYMLINK),
+       (PLen > MaxPath orelse PLen2 > MaxPath) ->
+    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NO_SUCH_PATH,
+                            "No such path"),
+    State;
 handle_op(?SSH_FXP_INIT, Version, B, State) when is_binary(B) ->
     XF = State#state.xf,
     Vsn = lists:min([XF#ssh_xfer.vsn, Version]),
@@ -212,7 +274,7 @@ handle_op(?SSH_FXP_INIT, Version, B, Sta
     ssh_xfer:xf_send_reply(XF1, ?SSH_FXP_VERSION, <<?UINT32(Vsn)>>),
     State#state{xf = XF1};
 handle_op(?SSH_FXP_REALPATH, ReqId,
-	  <<?UINT32(Rlen), RPath:Rlen/binary>>,
+	  <<?UINT32(RLen), RPath:RLen/binary>>,
 	  State0) ->
     RelPath = relate_file_name(RPath, State0, _Canonicalize=false),
     {Res, State} = resolve_symlinks(RelPath, State0),
@@ -228,14 +290,16 @@ handle_op(?SSH_FXP_REALPATH, ReqId,
     end;
 handle_op(?SSH_FXP_OPENDIR, ReqId,
 	 <<?UINT32(RLen), RPath:RLen/binary>>,
-	  State0 = #state{xf = #ssh_xfer{vsn = Vsn}, 
-			  file_handler = FileMod, file_state = FS0}) ->
+	  State0 = #state{xf = #ssh_xfer{vsn = Vsn},
+			  file_handler = FileMod, file_state = FS0,
+                          max_handles = MaxHandles}) ->
     RelPath = unicode:characters_to_list(RPath),
     AbsPath = relate_file_name(RelPath, State0),
     
     XF = State0#state.xf,
     {IsDir, FS1} = FileMod:is_dir(AbsPath, FS0),
     State1 = State0#state{file_state = FS1},
+    HandlesCnt = length(State0#state.handles),
     case IsDir of
 	false when Vsn > 5 ->
 	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NOT_A_DIRECTORY,
@@ -245,8 +309,12 @@ handle_op(?SSH_FXP_OPENDIR, ReqId,
 	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_FAILURE,
 				    "Not a directory"),
 	    State1;
-	true ->
-	    add_handle(State1, XF, ReqId, directory, {RelPath,unread})
+	true when HandlesCnt < MaxHandles ->
+	    add_handle(State1, XF, ReqId, directory, {RelPath,unread});
+        true ->
+	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_FAILURE,
+				    "max_handles limit reached"),
+	    State1
     end;
 handle_op(?SSH_FXP_READDIR, ReqId,
 	  <<?UINT32(HLen), BinHandle:HLen/binary>>,
@@ -381,14 +449,12 @@ handle_op(?SSH_FXP_RMDIR, ReqId, <<?UINT
     send_status(Status, ReqId, State1);
 
 handle_op(?SSH_FXP_RENAME, ReqId,
-  	  Bin = <<?UINT32(PLen), _:PLen/binary, ?UINT32(PLen2),
-  		 _:PLen2/binary>>,
+  	  Bin = <<?UINT32(PLen), _:PLen/binary, ?UINT32(PLen2), _:PLen2/binary>>,
   	  State = #state{xf = #ssh_xfer{vsn = Vsn}}) when Vsn==3; Vsn==4  ->
     handle_op(?SSH_FXP_RENAME, ReqId, <<Bin/binary, 0:32>>, State);
 
 handle_op(?SSH_FXP_RENAME, ReqId,
-	  <<?UINT32(PLen), BPath:PLen/binary, ?UINT32(PLen2), 
-	   BPath2:PLen2/binary, ?UINT32(Flags)>>,
+	  <<?UINT32(PLen), BPath:PLen/binary, ?UINT32(PLen2), BPath2:PLen2/binary, ?UINT32(Flags)>>,
 	  State0 = #state{file_handler = FileMod, file_state = FS0}) ->
     Path = relate_file_name(BPath, State0),
     Path2 = relate_file_name(BPath2, State0),
@@ -426,23 +492,29 @@ handle_op(?SSH_FXP_SYMLINK, ReqId,
     State1 = State0#state{file_state = FS1},
     send_status(Status, ReqId, State1).
 
-new_handle([], H) ->
-    H;
-new_handle([{N, _,_} | Rest], H) when N =< H ->
-    new_handle(Rest, N+1);
-new_handle([_ | Rest], H) ->
-    new_handle(Rest, H).
+new_handle_id([]) -> 0;
+new_handle_id([{_, _, _} | _] = Handles) ->
+    {HandleIds, _, _} = lists:unzip3(Handles),
+    new_handle_id(lists:sort(HandleIds));
+new_handle_id(HandleIds) ->
+    find_gap(HandleIds).
+
+find_gap([Id]) -> % no gap found
+    Id + 1;
+find_gap([Id1, Id2 | _]) when Id2 - Id1 > 1 -> % gap found
+    Id1 + 1;
+find_gap([_, Id | Rest]) ->
+    find_gap([Id | Rest]).
 
 add_handle(State, XF, ReqId, Type, DirFileTuple) ->
     Handles = State#state.handles,
-    Handle = new_handle(Handles, 0),
-    ssh_xfer:xf_send_handle(XF, ReqId, integer_to_list(Handle)),
-    %% OBS: If you change handles-tuple also change new_handle!
-    %% Is this this the best way to implement new handle?
-    State#state{handles = [{Handle, Type, DirFileTuple} | Handles]}.
+    HandleId = new_handle_id(Handles),
+    ssh_xfer:xf_send_handle(XF, ReqId, integer_to_list(HandleId)),
+    %% OBS: If you change handles-tuple also change new_handle_id!
+    State#state{handles = [{HandleId, Type, DirFileTuple} | Handles]}.
     
 get_handle(Handles, BinHandle) ->
-    case (catch list_to_integer(binary_to_list(BinHandle))) of
+    case (catch binary_to_integer(BinHandle)) of
 	I when is_integer(I) ->
 	    case lists:keysearch(I, 1, Handles) of
 		{value, T} -> T;
@@ -564,7 +636,10 @@ get_attrs(RelPath, [F | Rest], FileMod,
 	     end,
 	    Attrs = ssh_sftp:info_to_attr(Info),
 	    get_attrs(RelPath, Rest, FileMod, FS1, Vsn, [{Name, Attrs} | Acc]);
-	{{error, enoent}, FS1} ->
+	{{error, Msg}, FS1} when 
+              Msg == enoent ;   % The item has disappeared after reading the list of items to check
+              Msg == eacces ->  % You are not allowed to read this
+            %% Skip this F and check the remaining Rest
 	    get_attrs(RelPath, Rest, FileMod, FS1, Vsn, Acc);
 	{Error, FS1} ->
 	    {Error, FS1}
@@ -694,7 +769,9 @@ open(Vsn, ReqId, Data, State) when Vsn >
     do_open(ReqId, State, Path, Flags).
 
 do_open(ReqId, State0, Path, Flags) ->
-    #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn}} = State0,
+    #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn},
+           max_handles = MaxHandles} = State0,
+    HandlesCnt = length(State0#state.handles),
     AbsPath = relate_file_name(Path, State0),
     {IsDir, _FS1} = FileMod:is_dir(AbsPath, FS0),
     case IsDir of 
@@ -706,7 +783,7 @@ do_open(ReqId, State0, Path, Flags) ->
 	    ssh_xfer:xf_send_status(State0#state.xf, ReqId,
 				    ?SSH_FX_FAILURE, "File is a directory"),
 	    State0;
-	false ->
+	false when HandlesCnt < MaxHandles ->
 	    OpenFlags = [binary | Flags],
 	    {Res, FS1} = FileMod:open(AbsPath, OpenFlags, FS0),
 	    State1 = State0#state{file_state = FS1},
@@ -717,7 +794,11 @@ do_open(ReqId, State0, Path, Flags) ->
 		    ssh_xfer:xf_send_status(State1#state.xf, ReqId,
 					    ssh_xfer:encode_erlang_status(Error)),
 		    State1
-	    end
+	    end;
+        false ->
+	    ssh_xfer:xf_send_status(State0#state.xf, ReqId,
+				    ?SSH_FX_FAILURE, "max_handles limit reached"),
+	    State0
     end.
 
 %% resolve all symlinks in a path
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_sftp.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_sftp.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_sftp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -58,9 +58,9 @@
 -record(state,
 	{
 	  xf,
-	  rep_buf = <<>>,
+	  rep_buf = <<>> :: binary(),
 	  req_id,
-	  req_list = [],  %% {ReqId, Fun}
+	  req_list = [], %% {ReqId, Fun}
 	  inf,   %% list of fileinf,
 	  opts
 	 }).
@@ -75,14 +75,14 @@
 
 -record(bufinf,
 	{
-	  mode,			 % read | write  (=from or to buffer by user)
-	  crypto_state,
+	  mode                  :: read | write, % read | write  (=from or to buffer by user)
+	  crypto_state          :: term() | undefined,
 	  crypto_fun,            % For encode or decode depending on the mode field
-	  size = 0,		 % # bytes "before" the current buffer for the postion call
+	  size = 0              :: non_neg_integer() | undefined, % # bytes "before" the current buffer for the position call
 
-	  chunksize,		 % The size of the chunks to be sent or received
-	  enc_text_buf = <<>>,	 % Encrypted text
-	  plain_text_buf = <<>>	 % Decrypted text
+	  chunksize             :: non_neg_integer() | undefined, % The size of the chunks to be sent or received
+	  enc_text_buf = <<>>   :: binary() | undefined,          % Encrypted text
+	  plain_text_buf = <<>> :: binary() | undefined           % Decrypted text
 	}).
 
 -define(FILEOP_TIMEOUT, infinity).
@@ -111,16 +111,9 @@
 %%%----------------------------------------------------------------
 %%% start_channel/1
 
-start_channel(Cm) when is_pid(Cm) ->
-    start_channel(Cm, []);
+start_channel(Dest) ->
+    start_channel(Dest, []).
  
-start_channel(Socket) when is_port(Socket) ->
-    start_channel(Socket, []);
-
-start_channel(Host) ->
-    start_channel(Host, []).
-
-
 %%%----------------------------------------------------------------
 %%% start_channel/2
 
@@ -128,7 +121,7 @@ start_channel(Host) ->
 %%% function clauses.
 
 -spec start_channel(ssh:open_socket(),
-                    [ssh:client_options() | sftp_option()]
+                    [ssh:client_option() | sftp_option()]
                    )
                    -> {ok,pid(),ssh:connection_ref()} | {error,reason()};
 
@@ -138,25 +131,10 @@ start_channel(Host) ->
                    -> {ok,pid()}  | {ok,pid(),ssh:connection_ref()} | {error,reason()};
 
                    (ssh:host(),
-                    [ssh:client_options() | sftp_option()]
+                    [ssh:client_option() | sftp_option()]
                    )
                    -> {ok,pid(),ssh:connection_ref()} | {error,reason()} .
 
-start_channel(Socket, UserOptions0) when is_port(Socket) ->
-    UserOptions = legacy_timeout(UserOptions0),
-    Timeout = proplists:get_value(connect_timeout, UserOptions, infinity),
-    {SshOpts, ChanOpts, SftpOpts} = handle_options(UserOptions),
-    case ssh:connect(Socket, SshOpts, Timeout) of
-	{ok,Cm} ->
-	    case start_channel(Cm, ChanOpts ++ SftpOpts) of
-		{ok, Pid} ->
-		    {ok, Pid, Cm};
-		Error ->
-		    Error
-	    end;
-	Error ->
-	    Error
-    end;
 start_channel(Cm, UserOptions0) when is_pid(Cm) ->
     UserOptions = legacy_timeout(UserOptions0),
     Timeout = proplists:get_value(timeout, UserOptions, infinity),
@@ -181,8 +159,29 @@ start_channel(Cm, UserOptions0) when is_
 	    Error
     end;
 
-start_channel(Host, UserOptions) ->
-    start_channel(Host, 22, UserOptions).
+start_channel(Dest, UserOptions0) ->
+    UserOptions = legacy_timeout(UserOptions0),
+    {SshOpts, ChanOpts, SftpOpts} = handle_options(UserOptions),
+    case ssh:is_host(Dest, SshOpts) of
+        true ->
+            %% Dest looks like is a Host
+            start_channel(Dest, 22, UserOptions);
+        false ->
+            %% No, it is probably not a Host, must be a socket
+            Socket = Dest,
+            Timeout = proplists:get_value(connect_timeout, SshOpts, infinity),
+            case ssh:connect(Socket, SshOpts, Timeout) of
+                {ok,Cm} ->
+                    case start_channel(Cm, ChanOpts ++ SftpOpts) of
+                        {ok, Pid} ->
+                            {ok, Pid, Cm};
+                        Error ->
+                            Error
+                    end;
+                Error ->
+                    Error
+            end
+    end.
 
 
 %%%----------------------------------------------------------------
@@ -230,8 +229,8 @@ stop_channel(Pid) ->
             receive {'DOWN',MonRef,_,_,_} -> ok
             after
                 1000 ->
-                    exit(Pid, kill),
                     erlang:demonitor(MonRef, [flush]),
+                    exit(Pid, kill),
                     ok
             end;
 	false ->
@@ -777,14 +776,15 @@ read_file(Pid, Name) ->
       Timeout :: timeout(),
       Error :: {error, reason()}.
 read_file(Pid, Name, FileOpTimeout) ->
-    case open(Pid, Name, [read, binary], FileOpTimeout) of
-	{ok, Handle} ->
-	    {ok,{_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
-	    Res = read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, []),
-	    close(Pid, Handle),
-	    Res;
-	Error ->
-	    Error
+    try
+        {ok, Handle} = open(Pid, Name, [read, binary], FileOpTimeout),
+        {ok, {_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
+        Res = read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, []),
+        close(Pid, Handle),
+        Res
+    catch
+        error:{badmatch, Error = {error,_}} ->
+            Error
     end.
 
 read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, Acc) ->
@@ -814,15 +814,16 @@ write_file(Pid, Name, List) ->
 write_file(Pid, Name, List, FileOpTimeout) when is_list(List) ->
     write_file(Pid, Name, to_bin(List), FileOpTimeout);
 write_file(Pid, Name, Bin, FileOpTimeout) ->
-    case open(Pid, Name, [write, binary], FileOpTimeout) of
-	{ok, Handle} ->
-	    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-	    Res = write_file_loop(Pid, Handle, 0, Bin, size(Bin), Packet,
-				  FileOpTimeout),
-	    close(Pid, Handle, FileOpTimeout),
-	    Res;
-	Error ->
-	    Error
+    try
+        {ok, Handle} = open(Pid, Name, [write, binary], FileOpTimeout),
+        {ok, {_Window,Packet}} = send_window(Pid, FileOpTimeout),
+        Res = write_file_loop(Pid, Handle, 0, Bin, byte_size(Bin), Packet,
+                              FileOpTimeout),
+        close(Pid, Handle, FileOpTimeout),
+        Res
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
     end.
 
 write_file_loop(_Pid, _Handle, _Pos, _Bin, 0, _PacketSz,_FileOpTimeout) ->
@@ -1018,7 +1019,7 @@ do_handle_call({pwrite,Async,Handle,At,D
 	{ok,Offset} ->
 	    Data = to_bin(Data0),
 	    ReqID = State#state.req_id,
-	    Size = size(Data),
+	    Size = byte_size(Data),
 	    ssh_xfer:write(?XF(State),ReqID,Handle,Offset,Data),
 	    State1 = update_size(Handle, Offset+Size, State),
 	    make_reply(ReqID, Async, From, State1);
@@ -1031,7 +1032,7 @@ do_handle_call({write,Async,Handle,Data0
 	{ok,Offset} ->
 	    Data = to_bin(Data0),
 	    ReqID = State#state.req_id,
-	    Size = size(Data),
+	    Size = byte_size(Data),
 	    ssh_xfer:write(?XF(State),ReqID,Handle,Offset,Data),
 	    State1 = update_offset(Handle, Offset+Size, State),
 	    make_reply(ReqID, Async, From, State1);
@@ -1225,10 +1226,10 @@ terminate(_Reason, State) ->
 %% Internal functions
 %%====================================================================
 legacy_timeout(UserOptions) ->
-    %% Make both connect_timeout and timeout defined if exaclty one of them is defined:
+    %% Make both connect_timeout and timeout defined if exactly one of them is defined:
     case {proplists:get_value(connect_timeout, UserOptions),
           proplists:get_value(timeout, UserOptions)} of
-        {undefined, undefined} ->                
+        {undefined, undefined} ->
             UserOptions;
         {undefined, TO} ->
             [{connect_timeout,TO} | UserOptions];
@@ -1583,7 +1584,7 @@ erase_handle(Handle, State) ->
     State#state{inf = FI}.
 
 %%
-%% Caluclate a integer offset
+%% Calculate a integer offset
 %%
 lseek_position(Handle, Pos, State) ->
     case maps:find(Handle, State#state.inf) of
@@ -1635,13 +1636,18 @@ to_bin(Data) when is_binary(Data) -> Dat
 
 
 read_repeat(Pid, Handle, Len, FileOpTimeout) ->
-    {ok,{_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
-    read_rpt(Pid, Handle, Len, PacketSz, FileOpTimeout, <<>>).
+    try
+        {ok,{_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
+        read_rpt(Pid, Handle, Len, PacketSz, FileOpTimeout, <<>>)
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
+    end.
 
 read_rpt(Pid, Handle, WantedLen, PacketSz, FileOpTimeout, Acc) when WantedLen > 0 ->
     case read(Pid, Handle, min(WantedLen,PacketSz), FileOpTimeout) of
 	{ok, Data}  ->
-	    read_rpt(Pid, Handle, WantedLen-size(Data), PacketSz, FileOpTimeout, <<Acc/binary, Data/binary>>);
+	    read_rpt(Pid, Handle, WantedLen-byte_size(Data), PacketSz, FileOpTimeout, <<Acc/binary, Data/binary>>);
 	eof ->
 	    {ok, Acc};
 	Error ->
@@ -1654,8 +1660,13 @@ read_rpt(_Pid, _Handle, WantedLen, _Pack
 write_to_remote_tar(_Pid, _SftpHandle, <<>>, _FileOpTimeout) ->
     ok;
 write_to_remote_tar(Pid, SftpHandle, Bin, FileOpTimeout) ->
-    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-    write_file_loop(Pid, SftpHandle, 0, Bin, size(Bin), Packet, FileOpTimeout).
+    try
+        {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
+        write_file_loop(Pid, SftpHandle, 0, Bin, byte_size(Bin), Packet, FileOpTimeout)
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
+    end.
 
 position_buf(Pid, SftpHandle, BufHandle, Pos, FileOpTimeout) ->
     {ok,#bufinf{mode = Mode,
@@ -1663,7 +1674,7 @@ position_buf(Pid, SftpHandle, BufHandle,
 		size = Size}} = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
     case Pos of
 	{cur,0} when Mode==write ->
-	    {ok,Size+size(Buf0)};
+	    {ok,Size+byte_size(Buf0)};
 
 	{cur,0} when Mode==read ->
 	    {ok,Size};
@@ -1692,23 +1703,29 @@ position_buf(Pid, SftpHandle, BufHandle,
       end.
 
 read_buf(Pid, SftpHandle, BufHandle, WantedLen, FileOpTimeout) ->
-    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-    {ok,B0}  = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
-    case do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout, B0) of
-	{ok,ResultBin,B} ->
-	    call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
-	    {ok,ResultBin};
-	{error,Error} ->
-	    {error,Error};
-	{eof,B} ->
-	    call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
-	    eof
-      end.
+    try
+        {ok, {_Window, Packet}} = send_window(Pid, FileOpTimeout),
+        {ok, B0} = call(Pid, {get_bufinf, BufHandle}, FileOpTimeout),
+        case do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout, B0) of
+            {ok, ResultBin, B} ->
+                call(Pid, {put_bufinf, BufHandle, B}, FileOpTimeout),
+                {ok, ResultBin};
+            {eof, B} ->
+                call(Pid, {put_bufinf, BufHandle, B}, FileOpTimeout),
+                eof
+        end
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error;
+        error:{case_clause, Error = {error, _}} ->
+            Error
+    end.
+
 
 do_the_read_buf(_Pid, _SftpHandle, WantedLen, _Packet, _FileOpTimeout,
 		B=#bufinf{plain_text_buf=PlainBuf0,
 			  size = Size})
-    when size(PlainBuf0) >= WantedLen ->
+    when byte_size(PlainBuf0) >= WantedLen ->
     %% We already have the wanted number of bytes decoded and ready!
     <<ResultBin:WantedLen/binary, PlainBuf/binary>> = PlainBuf0,
     {ok,ResultBin,B#bufinf{plain_text_buf=PlainBuf,
@@ -1719,8 +1736,8 @@ do_the_read_buf(Pid, SftpHandle, WantedL
 			   enc_text_buf = EncBuf0,
 			   chunksize = undefined
 			  })
-  when size(EncBuf0) > 0 ->
-    %% We have (at least) one decodable byte waiting for decodeing.
+  when byte_size(EncBuf0) > 0 ->
+    %% We have (at least) one decodable byte waiting for decoding.
     {ok,DecodedBin,B} = apply_crypto(EncBuf0, B0),
     do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout,
 		    B#bufinf{plain_text_buf = <<PlainBuf0/binary, DecodedBin/binary>>,
@@ -1732,8 +1749,8 @@ do_the_read_buf(Pid, SftpHandle, WantedL
 			   enc_text_buf = EncBuf0,
 			   chunksize = ChunkSize0
 			  })
-  when size(EncBuf0) >= ChunkSize0 ->
-    %% We have (at least) one chunk of decodable bytes waiting for decodeing.
+  when byte_size(EncBuf0) >= ChunkSize0 ->
+    %% We have (at least) one chunk of decodable bytes waiting for decoding.
     <<ToDecode:ChunkSize0/binary, EncBuf/binary>> = EncBuf0,
     {ok,DecodedBin,B} = apply_crypto(ToDecode, B0),
     do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout,
@@ -1755,21 +1772,22 @@ do_the_read_buf(Pid, SftpHandle, WantedL
 
 
 write_buf(Pid, SftpHandle, BufHandle, PlainBin, FileOpTimeout) ->
-    {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
-    {ok,B0=#bufinf{plain_text_buf=PTB}}  = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
-    case do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
-			  B0#bufinf{plain_text_buf = <<PTB/binary,PlainBin/binary>>}) of
-	{ok, B} ->
-	    call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
-	    ok;
-	{error,Error} ->
-	    {error,Error}
+    try
+        {ok, {_Window,Packet}} = send_window(Pid, FileOpTimeout),
+        {ok, B0=#bufinf{plain_text_buf=PTB}} = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
+        {ok, B} = do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+                                   B0#bufinf{plain_text_buf = <<PTB/binary,PlainBin/binary>>}),
+        call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
+        ok
+    catch
+        error:{badmatch, Error = {error, _}} ->
+            Error
     end.
 
 do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
 		 B=#bufinf{enc_text_buf = EncBuf0,
 			   size = Size})
-  when size(EncBuf0) >= Packet ->
+  when byte_size(EncBuf0) >= Packet ->
     <<BinToWrite:Packet/binary, EncBuf/binary>> = EncBuf0,
     case write(Pid, SftpHandle, BinToWrite, FileOpTimeout) of
 	ok ->
@@ -1784,7 +1802,7 @@ do_the_write_buf(Pid, SftpHandle, Packet
 		 B0=#bufinf{plain_text_buf = PlainBuf0,
 			    enc_text_buf = EncBuf0,
 			    chunksize = undefined})
-  when size(PlainBuf0) > 0 ->
+  when byte_size(PlainBuf0) > 0 ->
      {ok,EncodedBin,B} = apply_crypto(PlainBuf0, B0),
      do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
 		     B#bufinf{plain_text_buf = <<>>,
@@ -1795,7 +1813,7 @@ do_the_write_buf(Pid, SftpHandle, Packet
 			    enc_text_buf = EncBuf0,
 			    chunksize = ChunkSize0
 			   })
-  when size(PlainBuf0) >= ChunkSize0 ->
+  when byte_size(PlainBuf0) >= ChunkSize0 ->
     <<ToEncode:ChunkSize0/binary, PlainBuf/binary>> = PlainBuf0,
     {ok,EncodedBin,B} = apply_crypto(ToEncode, B0),
     do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_system_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_system_sup.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_system_sup.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -31,153 +31,243 @@
 
 -include("ssh.hrl").
 
--export([start_link/5, stop_listener/1,
-	 stop_listener/3, stop_system/2,
-	 stop_system/4, system_supervisor/3,
-	 subsystem_supervisor/1, channel_supervisor/1,
-	 connection_supervisor/1,
-	 acceptor_supervisor/1, start_subsystem/6,
-	 stop_subsystem/2,
-         get_options/4
+-export([start_link/3,
+         stop_listener/1,
+	 stop_system/1,
+         start_system/2,
+         start_connection/4,
+	 get_daemon_listen_address/1,
+         addresses/1,
+         get_options/2,
+         get_acceptor_options/1,
+         replace_acceptor_options/2
         ]).
 
 %% Supervisor callback
 -export([init/1]).
 
--define(START(Address, Port, Profile, Options),
-        {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]}).
-
 %%%=========================================================================
 %%% API
 %%%=========================================================================
-start_link(Role, Address, Port, Profile, Options) ->
-    Name = make_name(Address, Port, Profile),
-    supervisor:start_link({local, Name}, ?MODULE, [Role, Address, Port, Profile, Options]).
+
+start_system(Address0, Options) ->
+    case find_system_sup(Address0) of
+        {ok,{SysPid,Address}} ->
+            restart_acceptor(SysPid, Address, Options);
+        {error,not_found} ->
+            supervisor:start_child(sshd_sup,
+                                   #{id       => {?MODULE,Address0},
+                                     start    => {?MODULE, start_link, [server, Address0, Options]},
+                                     restart  => temporary,
+                                     type     => supervisor
+                                    })
+    end.
+
+%%%----------------------------------------------------------------
+stop_system(SysSup) when is_pid(SysSup) ->
+    case lists:keyfind(SysSup, 2, supervisor:which_children(sup(server))) of
+        {{?MODULE, Id}, SysSup, _, _} -> stop_system(Id);
+        false -> ok
+    end;
+stop_system(Id) ->
+    supervisor:terminate_child(sup(server), {?MODULE, Id}).
+
+
+%%%----------------------------------------------------------------
+stop_listener(SystemSup) when is_pid(SystemSup) ->
+    {Id, _, _, _} = lookup(ssh_acceptor_sup, SystemSup),
+    supervisor:terminate_child(SystemSup, Id),
+    supervisor:delete_child(SystemSup, Id).
+
+%%%----------------------------------------------------------------
+get_daemon_listen_address(SystemSup) ->
+    try lookup(ssh_acceptor_sup, SystemSup)
+    of
+        {{ssh_acceptor_sup,Address}, _, _, _} ->
+            {ok, Address};
+        _ ->
+            {error, not_found}
+    catch
+        _:_ ->
+            {error, not_found}
+    end.
+
+%%%----------------------------------------------------------------
+%%% Start the connection child. It is a significant child of the system
+%%% supervisor (callback = this module) for server and non-significant
+%%% child of sshc_sup for client
+start_connection(Role = client, _, Socket, Options) ->
+    do_start_connection(Role, sup(client), false, Socket, Options);
+start_connection(Role = server, Address=#address{}, Socket, Options) ->
+    case get_system_sup(Address, Options) of
+        {ok, SysPid} ->
+            do_start_connection(Role, SysPid, true, Socket, Options);
+        Others ->
+            Others
+    end.
+
+do_start_connection(Role, SupPid, Significant, Socket, Options0) ->
+    Id = make_ref(),
+    Options = ?PUT_INTERNAL_OPT([{user_pid, self()}], Options0),
+    case supervisor:start_child(SupPid,
+                                #{id          => Id,
+                                  start       => {ssh_connection_sup, start_link,
+                                                  [Role,Id,Socket,Options]
+                                                 },
+                                  restart     => temporary,
+                                  significant => Significant,
+                                  type        => supervisor
+                                 })
+    of
+        {ok,_ConnectionSupPid} ->
+            try
+                receive
+                    {new_connection_ref, Id, ConnPid} ->
+                        ssh_connection_handler:takeover(ConnPid, Role, Socket, Options)
+                after 10000 ->
+                        error(timeout)
+                end
+            catch
+                error:{badmatch,{error,Error}} ->
+                    {error,Error};
+                error:timeout ->
+                    %% The connection was started, but the takover procedure timed out,
+                    %% therefore it exists a subtree, but it is not quite ready and
+                    %% must be removed (by the supervisor above):
+                    supervisor:terminate_child(SupPid, Id),
+                    {error, connection_start_timeout}
+            end;
+        Others ->
+            Others
+    end.
+
+%%%----------------------------------------------------------------
+start_link(Role, Address, Options) ->
+    supervisor:start_link(?MODULE, [Role, Address, Options]).
+
+%%%----------------------------------------------------------------
+addresses(#address{address=Address, port=Port, profile=Profile}) ->
+    [{SysSup,A} || {{ssh_system_sup,A},SysSup,supervisor,_} <-
+                     supervisor:which_children(sshd_sup),
+                 Address == any orelse A#address.address == Address,
+                 Port == any    orelse A#address.port == Port,
+                 Profile == any orelse A#address.profile == Profile].
+
+%%%----------------------------------------------------------------
+%% SysPid is the DaemonRef
+get_acceptor_options(SysPid) ->
+    case get_daemon_listen_address(SysPid) of
+        {ok,Address} ->
+            get_options(SysPid, Address);
+        {error,not_found} ->
+            {error,bad_daemon_ref}
+    end.
+
+replace_acceptor_options(SysPid, NewOpts) ->
+    case get_daemon_listen_address(SysPid) of
+        {ok,Address} ->
+            try stop_listener(SysPid)
+            of
+                ok ->
+                    restart_acceptor(SysPid, Address, NewOpts)
+            catch
+                error:_ ->
+                    restart_acceptor(SysPid, Address, NewOpts)
+            end;
+        {error,Error} ->
+            {error,Error}
+    end.
 
 %%%=========================================================================
 %%%  Supervisor callback
 %%%=========================================================================
-init([server, Address, Port, Profile, Options]) ->
-    SupFlags = #{strategy  => one_for_one,
+init([Role, Address, Options]) ->
+    SupFlags = #{strategy      => one_for_one,
+                 auto_shutdown => all_significant,
                  intensity =>    0,
                  period    => 3600
                 },
     ChildSpecs =
-        case ?GET_INTERNAL_OPT(connected_socket,Options,undefined) of
-            undefined ->
-                [#{id       => id(ssh_acceptor_sup, Address, Port, Profile),
-                   start    => ?START(Address,Port,Profile,Options),
-                   restart  => transient,
-                   type     => supervisor
-                  }];
+        case {Role, is_socket_server(Options)} of
+            {server, false} ->
+                [acceptor_sup_child_spec(_SysSup=self(), Address, Options)];
             _ ->
                 []
         end,
-    {ok, {SupFlags,ChildSpecs}};
-
-init([client, _Address, _Port, _Profile, _Options]) ->
-    SupFlags = #{strategy  => one_for_one,
-                 intensity =>    0,
-                 period    => 3600
-                },
-    ChildSpecs = [],
     {ok, {SupFlags,ChildSpecs}}.
 
 %%%=========================================================================
 %%% Service API
 %%%=========================================================================
-stop_listener(SystemSup) ->
-    {Name, AcceptorSup, _, _} = lookup(ssh_acceptor_sup, SystemSup),
-    case supervisor:terminate_child(AcceptorSup, Name) of
-        ok ->
-            supervisor:delete_child(AcceptorSup, Name);
-        Error ->
-            Error
-    end.
-
-stop_listener(Address, Port, Profile) ->
-    stop_listener(
-      system_supervisor(Address, Port, Profile)).
-
 
-stop_system(server, SysSup) -> catch sshd_sup:stop_child(SysSup), ok;
-stop_system(client, SysSup) -> catch sshc_sup:stop_child(SysSup), ok.
+%% A macro to keep get_options/2 and acceptor_sup_child_spec/3 synchronized
+-define(accsup_start(SysSup,Addr,Opts),
+        {ssh_acceptor_sup, start_link, [SysSup,Addr,Opts]}
+       ).
 
-stop_system(server, Address, Port, Profile) ->
-    catch sshd_sup:stop_child(Address, Port, Profile),
-    ok.
-
-
-get_options(Sup, Address, Port, Profile) ->
+get_options(Sup, Address = #address{}) ->
+    %% Lookup the Option parameter in the running ssh_acceptor_sup:
     try
-       {ok, #{start:=?START(Address,Port,Profile,Options)}} =
-           supervisor:get_childspec(Sup, id(ssh_acceptor_sup,Address,Port,Profile)),
-       {ok, Options}
+        {ok, #{start:=?accsup_start(_, _, Options)}} =
+            supervisor:get_childspec(Sup, {ssh_acceptor_sup,Address}),
+        {ok, Options}
     catch
         _:_ -> {error,not_found}
     end.
 
-system_supervisor(Address, Port, Profile) ->
-    Name = make_name(Address, Port, Profile),
-    whereis(Name).
-
-subsystem_supervisor(SystemSup) ->
-    {_, Child, _, _} = lookup(ssh_subsystem_sup, SystemSup),
-    Child.
-
-channel_supervisor(SystemSup) ->
-    ssh_subsystem_sup:channel_supervisor(
-      subsystem_supervisor(SystemSup)).
-
-connection_supervisor(SystemSup) ->
-    ssh_subsystem_sup:connection_supervisor(
-      subsystem_supervisor(SystemSup)).
-
-acceptor_supervisor(SystemSup) ->
-    {_, Child, _, _} = lookup(ssh_acceptor_sup, SystemSup),
-    Child.
-
-
-start_subsystem(SystemSup, Role, Address, Port, Profile, Options) ->
-    SubsystemSpec =
-        #{id       => make_ref(),
-          start    => {ssh_subsystem_sup, start_link, [Role, Address, Port, Profile, Options]},
-          restart  => temporary,
-          type     => supervisor
-         },
-    supervisor:start_child(SystemSup, SubsystemSpec).
-
-stop_subsystem(SystemSup, SubSys) ->
-    case catch lists:keyfind(SubSys, 2, supervisor:which_children(SystemSup)) of
-	false ->
-	    {error, not_found};
-	{Id, _, _, _} ->
-	    spawn(fun() -> supervisor:terminate_child(SystemSup, Id),
-			   supervisor:delete_child(SystemSup, Id) end),
-	    ok;
-	{'EXIT', {noproc, _}} ->
-	    %% Already terminated; probably shutting down.
-	    ok;
-	{'EXIT', {shutdown, _}} ->
-	    %% Already shutting down.
-	    ok
-    end.
-
 %%%=========================================================================
 %%%  Internal functions
 %%%=========================================================================
-id(Sup, Address, Port, Profile) ->
-    {Sup, Address, Port, Profile}.
 
-make_name(Address, Port, Profile) ->
-    list_to_atom(lists:flatten(io_lib:format("ssh_system_~s_~p_~p_sup", [fmt_host(Address), Port, Profile]))).
+%% A separate function because this spec is need in >1 places
+acceptor_sup_child_spec(SysSup, Address, Options) ->
+    #{id       => {ssh_acceptor_sup,Address},
+      start    => ?accsup_start(SysSup, Address, Options),
+      restart  => transient,
+      significant => true,
+      type     => supervisor
+     }.
 
-fmt_host(IP) when is_tuple(IP) -> inet:ntoa(IP);
-fmt_host(A)  when is_atom(A)   -> A;
-fmt_host(S)  when is_list(S)   -> S.
+lookup(SupModule, SystemSup) ->
+    lists:keyfind([SupModule], 4, supervisor:which_children(SystemSup)).
 
+get_system_sup(Address0, Options) ->
+    case find_system_sup(Address0) of
+        {ok,{SysPid,_Address}} ->
+            {ok,SysPid};
+        {error,not_found} ->
+            start_system(Address0, Options);
+        {error,Error} ->
+            {error,Error}
+    end.
 
-lookup(SupModule, SystemSup) ->
-    lists:keyfind([SupModule], 4,
-                  supervisor:which_children(SystemSup)).
+find_system_sup(Address0) ->
+    case addresses(Address0) of
+        [{SysSupPid,Address}] ->
+            {ok,{SysSupPid,Address}};
+        [] -> {error,not_found};
+        [_,_|_] -> {error,ambiguous}
+    end.
 
+sup(client) -> sshc_sup;
+sup(server) -> sshd_sup.
+
+
+is_socket_server(Options) ->
+    undefined =/= ?GET_INTERNAL_OPT(connected_socket,Options,undefined).
+
+restart_acceptor(SysPid, Address, Options) ->
+    case lookup(ssh_acceptor_sup, SysPid) of
+        {_,_,supervisor,_} ->
+            {error, eaddrinuse};
+        false ->
+            ChildSpec = acceptor_sup_child_spec(SysPid, Address, Options),
+            case supervisor:start_child(SysPid, ChildSpec) of
+                {ok,_ChildPid} ->
+                    {ok,SysPid}; % sic!
+                {ok,_ChildPid,_Info} ->
+                    {ok,SysPid}; % sic!
+                {error,Error} ->
+                    {error,Error}
+            end
+    end.
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -35,9 +35,8 @@ supervised_start(FwdSup, {ListenAddrStr,
     case get_fwd_listen_opts(ListenAddrStr) of
         {ok,Opts} ->
             %% start listening on Addr:BoundPort
-            case gen_tcp:listen(ListenPort, [binary,
-                                             {reuseaddr,true},
-                                             {active,false} | Opts]) of
+            case gen_tcp:listen(ListenPort,
+                                Opts ++ [binary, {reuseaddr,true}, {active,false}]) of
                 {ok,LSock} ->
                     {ok,{_, TrueListenPort}} = inet:sockname(LSock),
                     ssh_tcpip_forward_acceptor_sup:start_child(FwdSup,
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_transport.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_transport.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_transport.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -26,12 +26,11 @@
 
 -include_lib("public_key/include/public_key.hrl").
 -include_lib("kernel/include/inet.hrl").
-
 -include("ssh_transport.hrl").
 -include("ssh.hrl").
 
 -export([versions/2, hello_version_msg/1]).
--export([next_seqnum/1, 
+-export([next_seqnum/3,
 	 supported_algorithms/0, supported_algorithms/1,
 	 default_algorithms/0, default_algorithms/1,
          clear_default_algorithms_env/0,
@@ -42,17 +41,18 @@
 	 key_exchange_init_msg/1,
 	 key_init/3, new_keys_message/1,
          ext_info_message/1,
-	 handle_kexinit_msg/3, handle_kexdh_init/2,
+	 handle_kexinit_msg/4, handle_kexdh_init/2,
 	 handle_kex_dh_gex_group/2, handle_kex_dh_gex_init/2, handle_kex_dh_gex_reply/2,
 	 handle_new_keys/2, handle_kex_dh_gex_request/2,
 	 handle_kexdh_reply/2, 
 	 handle_kex_ecdh_init/2,
 	 handle_kex_ecdh_reply/2,
          parallell_gen_key/1,
-	 extract_public_key/1,
 	 ssh_packet/2, pack/2,
          valid_key_sha_alg/3,
-	 sha/1, sign/3, verify/5,
+	 sign/3, sign/4,
+         verify/5,
+	 sha/1,
          get_host_key/2,
          call_KeyCb/3,
          public_algo/1]).
@@ -60,6 +60,8 @@
 -behaviour(ssh_dbg).
 -export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
 
+-define(MIN_DH_KEY_SIZE, 400).
+
 %%% For test suites
 -export([pack/3, adjust_algs_for_peer_version/2]).
 
@@ -86,33 +88,45 @@ clear_default_algorithms_env() ->
                                   | no_return() %  error(Reason)
                                   .
 default_algorithms() ->
+    FipsMode = crypto:info_fips(),
     case application:get_env(ssh, ?DEFAULT_ALGS) of
         undefined ->
-            %% Not cached, have to build the default, connection independent
-            %% set of algorithms:
-            Opts = get_alg_conf(),
-            Algs1 =
-                case proplists:get_value(preferred_algorithms, Opts) of
-                    undefined ->
-                        [{K,default_algorithms1(K)} || K <- algo_classes()];
-                    Algs0 ->
-                        {true,Algs01} = ssh_options:check_preferred_algorithms(Algs0),
-                        Algs01
-                end,
-            Algs =
-                case proplists:get_value(modify_algorithms, Opts) of
-                    undefined ->
-                        Algs1;
-                    Modifications ->
-                        ssh_options:initial_default_algorithms(Algs1, Modifications)
-                end,
-            application:set_env(ssh, ?DEFAULT_ALGS, Algs),
+            Algs = build_cache(),
+            application:set_env(ssh, ?DEFAULT_ALGS, {FipsMode,Algs}),
             Algs;
 
-        {ok,Algs} ->
+        {ok,{FipsMode,Algs}} ->
+            %% Cached, and the FIPS mode is the same now as when it was cached.
+            Algs;
+
+        {ok,{_OtherFipsMode,_Algs}} ->
+            %% Cached, but the FIPS mode has changed.
+            Algs = build_cache(),
+            application:set_env(ssh, ?DEFAULT_ALGS, {FipsMode,Algs}),
             Algs
     end.
 
+build_cache() ->
+    Opts = get_alg_conf(),
+    Algs1 =
+        case proplists:get_value(preferred_algorithms, Opts) of
+            undefined ->
+                [{K,default_algorithms1(K)} || K <- algo_classes()];
+            Algs0 ->
+                {true,Algs01} = ssh_options:check_preferred_algorithms(Algs0),
+                Algs01
+        end,
+    Algs =
+        case proplists:get_value(modify_algorithms, Opts) of
+            undefined ->
+                Algs1;
+            Modifications ->
+                ssh_options:initial_default_algorithms(Algs1, Modifications)
+        end,
+    Algs.
+
+
+
 get_alg_conf() ->
     [{T,L} || T <- [preferred_algorithms, modify_algorithms],
               L <- [application:get_env(ssh, T, [])],
@@ -137,10 +151,17 @@ algo_two_spec_class(_) -> false.
 
 
 default_algorithms(Tag) ->
+    FipsMode = crypto:info_fips(),
     case application:get_env(ssh, ?DEFAULT_ALGS) of
         undefined ->
             default_algorithms1(Tag);
-        {ok,Algs} ->
+        {ok,{FipsMode,Algs}} ->
+            %% Cached, and the FIPS mode is the same now as when it was cached.
+            proplists:get_value(Tag, Algs, []);
+        {ok,{_OtherFipsMode,_Algs}} ->
+            %% Cached, but the FIPS mode has changed.
+            Algs = build_cache(),
+            application:set_env(ssh, ?DEFAULT_ALGS, {FipsMode,Algs}),
             proplists:get_value(Tag, Algs, [])
     end.
     
@@ -166,6 +187,7 @@ default_algorithms1(mac) ->
 
 default_algorithms1(public_key) ->
     supported_algorithms(public_key, [
+                                      'ssh-rsa',
                                       %% Gone in OpenSSH 7.3.p1:
                                       'ssh-dss'
                                      ]);
@@ -179,18 +201,16 @@ supported_algorithms() -> [{K,supported_
 supported_algorithms(kex) ->
     select_crypto_supported(
       [
-       {'ecdh-sha2-nistp384',                   [{public_keys,ecdh}, {curves,secp384r1}, {hashs,sha384}]},
+       {'curve25519-sha256',                    [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
+       {'curve25519-sha256@libssh.org',         [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
+       {'curve448-sha512',                      [{public_keys,ecdh}, {curves,x448},   {hashs,sha512}]},
        {'ecdh-sha2-nistp521',                   [{public_keys,ecdh}, {curves,secp521r1}, {hashs,sha512}]},
+       {'ecdh-sha2-nistp384',                   [{public_keys,ecdh}, {curves,secp384r1}, {hashs,sha384}]},
        {'ecdh-sha2-nistp256',                   [{public_keys,ecdh}, {curves,secp256r1}, {hashs,sha256}]},
        {'diffie-hellman-group-exchange-sha256', [{public_keys,dh},   {hashs,sha256}]},
        {'diffie-hellman-group16-sha512',        [{public_keys,dh},   {hashs,sha512}]}, % In OpenSSH 7.3.p1
        {'diffie-hellman-group18-sha512',        [{public_keys,dh},   {hashs,sha512}]}, % In OpenSSH 7.3.p1
        {'diffie-hellman-group14-sha256',        [{public_keys,dh},   {hashs,sha256}]}, % In OpenSSH 7.3.p1
-       %% https://tools.ietf.org/html/draft-ietf-curdle-ssh-curves
-       %% Secure Shell (SSH) Key Exchange Method using Curve25519 and Curve448
-       {'curve25519-sha256',                    [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
-       {'curve25519-sha256@libssh.org',         [{public_keys,ecdh}, {curves,x25519}, {hashs,sha256}]},
-       {'curve448-sha512',                      [{public_keys,ecdh}, {curves,x448},   {hashs,sha512}]},
        {'diffie-hellman-group14-sha1',          [{public_keys,dh},   {hashs,sha}]},
        {'diffie-hellman-group-exchange-sha1',   [{public_keys,dh},   {hashs,sha}]},
        {'diffie-hellman-group1-sha1',           [{public_keys,dh},   {hashs,sha}]}
@@ -198,13 +218,13 @@ supported_algorithms(kex) ->
 supported_algorithms(public_key) ->
     select_crypto_supported(
       [
-       {'ecdsa-sha2-nistp384',  [{public_keys,ecdsa}, {hashs,sha384}, {curves,secp384r1}]},
-       {'ecdsa-sha2-nistp521',  [{public_keys,ecdsa}, {hashs,sha512}, {curves,secp521r1}]},
-       {'ecdsa-sha2-nistp256',  [{public_keys,ecdsa}, {hashs,sha256}, {curves,secp256r1}]},
        {'ssh-ed25519',          [{public_keys,eddsa}, {curves,ed25519}                    ]},
        {'ssh-ed448',            [{public_keys,eddsa}, {curves,ed448}                      ]},
-       {'rsa-sha2-256',         [{public_keys,rsa},   {hashs,sha256}                      ]},
+       {'ecdsa-sha2-nistp521',  [{public_keys,ecdsa}, {hashs,sha512}, {curves,secp521r1}]},
+       {'ecdsa-sha2-nistp384',  [{public_keys,ecdsa}, {hashs,sha384}, {curves,secp384r1}]},
+       {'ecdsa-sha2-nistp256',  [{public_keys,ecdsa}, {hashs,sha256}, {curves,secp256r1}]},
        {'rsa-sha2-512',         [{public_keys,rsa},   {hashs,sha512}                      ]},
+       {'rsa-sha2-256',         [{public_keys,rsa},   {hashs,sha256}                      ]},
        {'ssh-rsa',              [{public_keys,rsa},   {hashs,sha}                         ]},
        {'ssh-dss',              [{public_keys,dss},   {hashs,sha}                         ]} % Gone in OpenSSH 7.3.p1
       ]);
@@ -213,7 +233,6 @@ supported_algorithms(cipher) ->
     same(
       select_crypto_supported(
 	[
-         {'chacha20-poly1305@openssh.com', [{ciphers,chacha20}, {macs,poly1305}]},
          {'aes256-gcm@openssh.com', [{ciphers,aes_256_gcm}]},
          {'aes256-ctr',       [{ciphers,aes_256_ctr}]},
          {'aes192-ctr',       [{ciphers,aes_192_ctr}]},
@@ -221,6 +240,7 @@ supported_algorithms(cipher) ->
 	 {'aes128-ctr',       [{ciphers,aes_128_ctr}]},
 	 {'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]},
 	 {'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]},
+         {'chacha20-poly1305@openssh.com', [{ciphers,chacha20}, {macs,poly1305}]},
 	 {'aes256-cbc',       [{ciphers,aes_256_cbc}]},
 	 {'aes192-cbc',       [{ciphers,aes_192_cbc}]},
 	 {'aes128-cbc',       [{ciphers,aes_128_cbc}]},
@@ -230,15 +250,15 @@ supported_algorithms(cipher) ->
 supported_algorithms(mac) ->
     same(
       select_crypto_supported(
-	[{'hmac-sha2-256-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
-         {'hmac-sha2-512-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
-         {'hmac-sha2-256',    [{macs,hmac}, {hashs,sha256}]},
+	[{'hmac-sha2-512-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
+         {'hmac-sha2-256-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
 	 {'hmac-sha2-512',    [{macs,hmac}, {hashs,sha512}]},
+         {'hmac-sha2-256',    [{macs,hmac}, {hashs,sha256}]},
          {'hmac-sha1-etm@openssh.com', [{macs,hmac}, {hashs,sha256}]},
 	 {'hmac-sha1',        [{macs,hmac}, {hashs,sha}]},
 	 {'hmac-sha1-96',     [{macs,hmac}, {hashs,sha}]},
-	 {'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]},
-	 {'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]}
+         {'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]},
+	 {'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]}
 	]
        ));
 supported_algorithms(compression) ->
@@ -274,14 +294,19 @@ random_id(Nlo, Nup) ->
 hello_version_msg(Data) ->
     [Data,"\r\n"].
 
-next_seqnum(SeqNum) ->
+next_seqnum({State, _Role, init}, 16#ffffffff,
+            #ssh{algorithms = #alg{kex_strict_negotiated = true}})
+  when State == kexinit; State == key_exchange; State == new_keys ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: recv_sequence = 16#ffffffff", []));
+next_seqnum(_State, SeqNum, _) ->
     (SeqNum + 1) band 16#ffffffff.
 
 is_valid_mac(_, _ , #ssh{recv_mac_size = 0}) ->
     true;
 is_valid_mac(Mac, Data, #ssh{recv_mac = Algorithm,
 			     recv_mac_key = Key, recv_sequence = SeqNum}) ->
-    crypto:equal_const_time(Mac, mac(Algorithm, Key, SeqNum, Data)).
+    ssh_lib:comp(Mac, mac(Algorithm, Key, SeqNum, Data)).
 
 handle_hello_version(Version) ->
     try
@@ -336,7 +361,8 @@ kexinit_message(Role, Random, Algs, Host
     #ssh_msg_kexinit{
 		  cookie = Random,
 		  kex_algorithms = to_strings( get_algs(kex,Algs) )
-                                   ++ kex_ext_info(Role,Opts),
+                                   ++ kex_ext_info(Role,Opts)
+                                   ++ kex_strict_alg(Role),
 		  server_host_key_algorithms = HostKeyAlgs,
 		  encryption_algorithms_client_to_server = c2s(cipher,Algs),
 		  encryption_algorithms_server_to_client = s2c(cipher,Algs),
@@ -353,7 +379,8 @@ s2c(Key, Algs) -> x2y(server2client, Key
 
 x2y(DirectionKey, Key, Algs) -> to_strings(proplists:get_value(DirectionKey, get_algs(Key,Algs))).
 
-get_algs(Key, Algs) -> proplists:get_value(Key, Algs, default_algorithms(Key)).
+get_algs(Key, {_FipsMode,Algs}) when is_list(Algs) ->  proplists:get_value(Key, Algs, default_algorithms(Key));
+get_algs(Key, Algs) when is_list(Algs) -> proplists:get_value(Key, Algs, default_algorithms(Key)).
 
 to_strings(L) -> lists:map(fun erlang:atom_to_list/1, L).
 
@@ -364,52 +391,69 @@ new_keys_message(Ssh0) ->
 
 
 handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
-                   #ssh{role = client} = Ssh) ->
+                   #ssh{role = client} = Ssh, ReNeg) ->
     try
-        {ok, Algorithms} = select_algorithm(client, Own, CounterPart, Ssh#ssh.opts),
+        {ok, Algorithms} =
+            select_algorithm(client, Own, CounterPart, Ssh, ReNeg),
         true = verify_algorithm(Algorithms),
+        true = verify_kexinit_is_first_msg(Algorithms, Ssh, ReNeg),
         Algorithms
     of
 	Algos ->
 	    key_exchange_first_msg(Algos#alg.kex, 
 				   Ssh#ssh{algorithms = Algos})
     catch
-        Class:Error ->
-            Msg = kexinit_error(Class, Error, client, Own, CounterPart),
+        Class:Reason0 ->
+            Reason = ssh_lib:trim_reason(Reason0),
+            Msg = kexinit_error(Class, Reason, client, Own, CounterPart, Ssh),
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, Msg)
         end;
 
 handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
-                   #ssh{role = server} = Ssh) ->
+                   #ssh{role = server} = Ssh, ReNeg) ->
     try
-        {ok, Algorithms} = select_algorithm(server, CounterPart, Own, Ssh#ssh.opts),
+        {ok, Algorithms} =
+            select_algorithm(server, CounterPart, Own, Ssh, ReNeg),
         true = verify_algorithm(Algorithms),
+        true = verify_kexinit_is_first_msg(Algorithms, Ssh, ReNeg),
         Algorithms
     of
 	Algos ->
             {ok, Ssh#ssh{algorithms = Algos}}
     catch
-        Class:Error ->
-            Msg = kexinit_error(Class, Error, server, Own, CounterPart),
+        Class:Reason0 ->
+            Reason = ssh_lib:trim_reason(Reason0),
+            Msg = kexinit_error(Class, Reason, server, Own, CounterPart, Ssh),
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, Msg)
     end.
 
-kexinit_error(Class, Error, Role, Own, CounterPart) ->
+kexinit_error(Class, Error, Role, Own, CounterPart, Ssh) ->
     {Fmt,Args} =
         case {Class,Error} of
             {error, {badmatch,{false,Alg}}} ->
                 {Txt,W,C} = alg_info(Role, Alg),
-                {"No common ~s algorithm,~n"
-                 "  we have:~n    ~s~n"
-                 "  peer have:~n    ~s~n",
-                 [Txt,
-                  lists:join(", ", element(W,Own)),
-                  lists:join(", ", element(C,CounterPart))
-                 ]};
+                MsgFun =
+                    fun(debug) ->
+                            {"No common ~s algorithm,~n"
+                             "  we have:~n    ~s~n"
+                             "  peer have:~n    ~s~n",
+                             [Txt,
+                              lists:join(", ", element(W,Own)),
+                              lists:join(", ", element(C,CounterPart))]};
+                       (_) ->
+                            {"No common ~s algorithm", [Txt]}
+                    end,
+                ?SELECT_MSG(MsgFun);
             _ ->
                 {"Kexinit failed in ~p: ~p:~p", [Role,Class,Error]}
         end,
-    io_lib:format(Fmt, Args).
+    try io_lib:format(Fmt, Args, [{chars_limit, ssh_lib:max_log_len(Ssh)}]) of
+        R -> R
+    catch
+        _:_ ->
+            io_lib:format("Kexinit failed in ~p: ~p:~p", [Role, Class, Error],
+                          [{chars_limit, ssh_lib:max_log_len(Ssh)}])
+    end.
 
 alg_info(client, Alg) ->
     alg_info(Alg);
@@ -459,6 +503,21 @@ verify_algorithm(#alg{kex = Kex}) ->
         false -> {false, "kex"}
     end.
 
+verify_kexinit_is_first_msg(#alg{kex_strict_negotiated = false}, _, _) ->
+    true;
+verify_kexinit_is_first_msg(#alg{kex_strict_negotiated = true}, _, renegotiate) ->
+    true;
+verify_kexinit_is_first_msg(#alg{kex_strict_negotiated = true},
+                            #ssh{send_sequence = 1, recv_sequence = 1},
+                            init) ->
+    true;
+verify_kexinit_is_first_msg(#alg{kex_strict_negotiated = true},
+                            #ssh{send_sequence = SendSequence,
+                                 recv_sequence = RecvSequence}, init) ->
+    error_logger:warning_report(
+      lists:concat(["KEX strict violation (", SendSequence, ", ", RecvSequence, ")."])),
+    {false, "kex_strict"}.
+
 %%%----------------------------------------------------------------
 %%%
 %%% Key exchange initialization
@@ -530,24 +589,34 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e
 	    {Public, Private} = generate_key(dh, [P,G,2*Sz]),
 	    K = compute_key(dh, E, Private, [P,G]),
 	    MyPrivHostKey = get_host_key(SignAlg, Opts),
-	    MyPubHostKey = extract_public_key(MyPrivHostKey),
+	    MyPubHostKey = ssh_file:extract_public_key(MyPrivHostKey),
             H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {E,Public,K}),
-            H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
-	    {SshPacket, Ssh1} = 
-		ssh_packet(#ssh_msg_kexdh_reply{public_host_key = {MyPubHostKey,SignAlg},
-						f = Public,
-						h_sig = H_SIG
-					       }, Ssh0),
-	    {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}},
-				     shared_secret = ssh_bits:mpint(K),
-				     exchanged_hash = H,
-				     session_id = sid(Ssh1, H)}};
-
+            case sign(H, SignAlg, MyPrivHostKey, Ssh0) of
+                {ok,H_SIG} ->
+                    {SshPacket, Ssh1} =
+                        ssh_packet(#ssh_msg_kexdh_reply{public_host_key = {MyPubHostKey,SignAlg},
+                                                        f = Public,
+                                                        h_sig = H_SIG
+                                                       }, Ssh0),
+                    {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}},
+                                             shared_secret = ssh_bits:mpint(K),
+                                             exchanged_hash = H,
+                                             session_id = sid(Ssh1, H)}};
+                {error,unsupported_sign_alg} ->
+                    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                                io_lib:format("Unsupported algorithm ~p", [SignAlg],
+                                              [{chars_limit, ssh_lib:max_log_len(Opts)}]))
+            end;
 	true ->
-            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+            MsgFun =
+                fun(debug) ->
                         io_lib:format("Kexdh init failed, received 'e' out of bounds~n  E=~p~n  P=~p",
-                                      [E,P])
-                       )
+                                      [E,P], [{chars_limit, ssh_lib:max_log_len(Opts)}]);
+                   (_) ->
+                        io_lib:format("Kexdh init failed, received 'e' out of bounds", [],
+                                      [{chars_limit, ssh_lib:max_log_len(Opts)}] )
+                end,
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, ?SELECT_MSG(MsgFun))
     end.
 
 handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey,
@@ -568,14 +637,15 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{
                                                              session_id = sid(Ssh, H)})};
 		Error ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                io_lib:format("Kexdh init failed. Verify host key: ~p",[Error])
+                                io_lib:format("Kexdh init failed. Verify host key: ~p",[Error],
+                                              [{chars_limit, ssh_lib:max_log_len(Ssh0)}])
                                )
 	    end;
 
 	true ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
                         io_lib:format("Kexdh init failed, received 'f' out of bounds~n  F=~p~n  P=~p",
-                                      [F,P])
+                                      [F,P], [{chars_limit, ssh_lib:max_log_len(Ssh0)}])
                        )
     end.
 
@@ -601,7 +671,8 @@ handle_kex_dh_gex_request(#ssh_msg_kex_d
 		    }};
 	{error,_} ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                        io_lib:format("No possible diffie-hellman-group-exchange group found",[])
+                        io_lib:format("No possible diffie-hellman-group-exchange group found",[],
+                                      [{chars_limit, ssh_lib:max_log_len(Opts)}])
                        )
     end;
 
@@ -633,8 +704,8 @@ handle_kex_dh_gex_request(#ssh_msg_kex_d
 		    }};
 	{error,_} ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                        io_lib:format("No possible diffie-hellman-group-exchange group found",[])
-                       )
+                        io_lib:format("No possible diffie-hellman-group-exchange group found",[],
+                                      [{chars_limit, ssh_lib:max_log_len(Opts)}]))
     end;
 
 handle_kex_dh_gex_request(_, _) ->
@@ -660,7 +731,6 @@ handle_kex_dh_gex_group(#ssh_msg_kex_dh_
     {Public, Private} = generate_key(dh, [P,G,2*Sz]),
     {SshPacket, Ssh1} = 
 	ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0),	% Pub = G^Priv mod P (def)
-
     {ok, SshPacket, 
      Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}.
 
@@ -677,27 +747,36 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_g
 	    if
 		1<K, K<(P-1) ->
 		    MyPrivHostKey = get_host_key(SignAlg, Opts),
-		    MyPubHostKey = extract_public_key(MyPrivHostKey),
+		    MyPubHostKey = ssh_file:extract_public_key(MyPrivHostKey),
                     H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {Min,NBits,Max,P,G,E,Public,K}),
-                    H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
-		    {SshPacket, Ssh} = 
-			ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = {MyPubHostKey,SignAlg},
-							     f = Public,
-							     h_sig = H_SIG}, Ssh0),
-		    {ok, SshPacket, Ssh#ssh{shared_secret = ssh_bits:mpint(K),
-					    exchanged_hash = H,
-					    session_id = sid(Ssh, H)
-					   }};
+                    case sign(H, SignAlg, MyPrivHostKey, Ssh0) of
+                        {ok,H_SIG} ->
+                            {SshPacket, Ssh} =
+                                ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = {MyPubHostKey,SignAlg},
+                                                                     f = Public,
+                                                                     h_sig = H_SIG}, Ssh0),
+                            {ok, SshPacket, Ssh#ssh{shared_secret = ssh_bits:mpint(K),
+                                                    exchanged_hash = H,
+                                                    session_id = sid(Ssh, H)
+                                                   }};
+                        {error,unsupported_sign_alg} ->
+                            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                                        io_lib:format("Unsupported algorithm ~p", [SignAlg],
+                                                     [{chars_limit, ssh_lib:max_log_len(Opts)}]))
+                    end;
 		true ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                "Kexdh init failed, received 'k' out of bounds"
-                               )
+                                "Kexdh init failed, received 'k' out of bounds")
 	    end;
 	true ->
-            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                        io_lib:format("Kexdh gex init failed, received 'e' out of bounds~n  E=~p~n  P=~p",
-                                      [E,P])
-                       )
+            MsgFun =
+                fun(debug) ->
+                        io_lib:format("Kexdh gex init failed, received 'e' out of bounds~n"
+                                      "  E=~p~n  P=~p", [E,P]);
+                   (_) ->
+                        io_lib:format("Kexdh gex init failed, received 'e' out of bounds", [])
+                end,
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, ?SELECT_MSG(MsgFun))
     end.
 
 handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostKey, 
@@ -722,20 +801,18 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_
                                                                      session_id = sid(Ssh, H)})};
                         Error ->
                             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                        io_lib:format("Kexdh gex reply failed. Verify host key: ~p",[Error])
-                                       )
+                                        io_lib:format("Kexdh gex reply failed. Verify host key: ~p",
+                                                      [Error], [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
 		    end;
 
 		true ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                "Kexdh gex init failed, 'K' out of bounds"
-                               )
+                                "Kexdh gex init failed, 'K' out of bounds")
 	    end;
 	true ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
                         io_lib:format("Kexdh gex init failed, received 'f' out of bounds~n  F=~p~n  P=~p",
-                                      [F,P])
-                       )
+                                      [F,P], [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
     end.
 
 %%%----------------------------------------------------------------
@@ -754,26 +831,40 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_i
     of
 	K ->
 	    MyPrivHostKey = get_host_key(SignAlg, Opts),
-	    MyPubHostKey = extract_public_key(MyPrivHostKey),
+	    MyPubHostKey = ssh_file:extract_public_key(MyPrivHostKey),
             H = kex_hash(Ssh0, MyPubHostKey, sha(Curve), {PeerPublic, MyPublic, K}),
-            H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
-	    {SshPacket, Ssh1} = 
-		ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = {MyPubHostKey,SignAlg},
-						   q_s = MyPublic,
-						   h_sig = H_SIG},
-			   Ssh0),
-    	    {ok, SshPacket, Ssh1#ssh{keyex_key = {{MyPublic,MyPrivate},Curve},
-				     shared_secret = ssh_bits:mpint(K),
-				     exchanged_hash = H,
-				     session_id = sid(Ssh1, H)}}
+            case sign(H, SignAlg, MyPrivHostKey, Ssh0) of
+                {ok,H_SIG} ->
+                    {SshPacket, Ssh1} =
+                        ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = {MyPubHostKey,SignAlg},
+                                                           q_s = MyPublic,
+                                                           h_sig = H_SIG},
+                                   Ssh0),
+                    {ok, SshPacket, Ssh1#ssh{keyex_key = {{MyPublic,MyPrivate},Curve},
+                                             shared_secret = ssh_bits:mpint(K),
+                                             exchanged_hash = H,
+                                             session_id = sid(Ssh1, H)}};
+                {error,unsupported_sign_alg} ->
+                    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                                io_lib:format("Unsupported algorithm ~p", [SignAlg],
+                                             [{chars_limit, ssh_lib:max_log_len(Opts)}]))
+            end
     catch
-        Class:Error ->
-            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+        Class:Reason0 ->
+            Reason = ssh_lib:trim_reason(Reason0),
+            MsgFun =
+                fun(debug) ->
                         io_lib:format("ECDH compute key failed in server: ~p:~p~n"
                                       "Kex: ~p, Curve: ~p~n"
                                       "PeerPublic: ~p",
-                                      [Class,Error,Kex,Curve,PeerPublic])
-                       )
+                                      [Class,Reason,Kex,Curve,PeerPublic],
+                                      [{chars_limit, ssh_lib:max_log_len(Ssh0)}]);
+                   (_) ->
+                        io_lib:format("ECDH compute key failed in server: ~p:~p",
+                                      [Class,Reason],
+                                      [{chars_limit, ssh_lib:max_log_len(Ssh0)}])
+                end,
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, ?SELECT_MSG(MsgFun))
     end.
 
 handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey,
@@ -796,15 +887,14 @@ handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_
                                                              session_id = sid(Ssh, H)})};
 		Error ->
                     ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
-                                io_lib:format("ECDH reply failed. Verify host key: ~p",[Error])
-                               )
+                                io_lib:format("ECDH reply failed. Verify host key: ~p",[Error],
+                                              [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
 	    end
     catch
         Class:Error ->
             ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
                         io_lib:format("Peer ECDH public key seem invalid: ~p:~p",
-                                      [Class,Error])
-                       )
+                                      [Class,Error], [{chars_limit, ssh_lib:max_log_len(Ssh0)}]))
     end.
 
 
@@ -821,6 +911,9 @@ handle_new_keys(#ssh_msg_newkeys{}, Ssh0
                        )
     end. 
 
+%%%----------------------------------------------------------------
+kex_strict_alg(client) -> [?kex_strict_c];
+kex_strict_alg(server) -> [?kex_strict_s].
 
 %%%----------------------------------------------------------------
 kex_ext_info(Role, Opts) ->
@@ -886,29 +979,11 @@ call_KeyCb(F, Args, Opts) ->
     UserOpts = ?GET_OPT(key_cb_options, Opts),
     apply(KeyCb, F, Args ++ [[{key_cb_private,KeyCbOpts}|UserOpts]]).
 
-extract_public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
-    #'RSAPublicKey'{modulus = N, publicExponent = E};
-extract_public_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
-    {Y,  #'Dss-Parms'{p=P, q=Q, g=G}};
-extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
-				   publicKey = Q}) when is_tuple(OID) ->
-    {#'ECPoint'{point=Q}, {namedCurve,OID}};
-extract_public_key({ed_pri, Alg, Pub, _Priv}) ->
-    {ed_pub, Alg, Pub};
-extract_public_key(#{engine:=_, key_id:=_, algorithm:=Alg} = M) ->
-    case {Alg, crypto:privkey_to_pubkey(Alg, M)} of
-        {rsa, [E,N]} ->
-            #'RSAPublicKey'{modulus = N, publicExponent = E};
-        {dss, [P,Q,G,Y]} ->
-            {Y, #'Dss-Parms'{p=P, q=Q, g=G}}
-    end.
-
-
 
 verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature}) ->
     case atom_to_list(Alg#alg.hkey) of
         AlgStr ->
-            case verify(Digest, sha(Alg#alg.hkey), Signature, PublicKey, SSH) of
+            case verify(Digest, Alg#alg.hkey, Signature, PublicKey, SSH) of
                 false ->
                     {error, bad_signature};
                 true ->
@@ -938,31 +1013,31 @@ accepted_host(Ssh, PeerName, Port, Publi
                                    "~s host key fingerprint is ~s.~n"
                                    "New host ~p~p accept",
                                    [fmt_hostkey(HostKeyAlg),
-                                    public_key:ssh_hostkey_fingerprint(Alg,Public),
+                                    ssh:hostkey_fingerprint(Alg,Public),
                                     PeerName, PortStr]),
             yes == yes_no(Ssh, Prompt);
 
         %% Call-back alternatives: A user provided fun is called for the decision:
         F when is_function(F,2) ->
-            case catch F(PeerName, public_key:ssh_hostkey_fingerprint(Public)) of
+            case catch F(PeerName, ssh:hostkey_fingerprint(Public)) of
                 true -> true;
                 _ -> {error, fingerprint_check_failed}
             end;
 
         F when is_function(F,3) ->
-            case catch F(PeerName, Port, public_key:ssh_hostkey_fingerprint(Public)) of
+            case catch F(PeerName, Port, ssh:hostkey_fingerprint(Public)) of
                 true -> true;
                 _ -> {error, fingerprint_check_failed}
             end;
 
 	{DigestAlg,F} when is_function(F,2) ->
-            case catch F(PeerName, public_key:ssh_hostkey_fingerprint(DigestAlg,Public)) of
+            case catch F(PeerName, ssh:hostkey_fingerprint(DigestAlg,Public)) of
                 true -> true;
                 _ -> {error, {fingerprint_check_failed,DigestAlg}}
             end;
 
 	{DigestAlg,F} when is_function(F,3) ->
-            case catch F(PeerName, Port, public_key:ssh_hostkey_fingerprint(DigestAlg,Public)) of
+            case catch F(PeerName, Port, ssh:hostkey_fingerprint(DigestAlg,Public)) of
                 true -> true;
                 _ -> {error, {fingerprint_check_failed,DigestAlg}}
             end
@@ -1029,7 +1104,33 @@ known_host_key(#ssh{opts = Opts, peer =
 %%
 %%   The first algorithm in each list MUST be the preferred (guessed)
 %%   algorithm.  Each string MUST contain at least one algorithm name.
-select_algorithm(Role, Client, Server, Opts) ->
+select_algorithm(Role, Client, Server,
+                 #ssh{opts = Opts,
+                      kex_strict_negotiated = KexStrictNegotiated0},
+                 ReNeg) ->
+    KexStrictNegotiated =
+        case ReNeg of
+            %% KEX strict negotiated once per connection
+            init ->
+                Result =
+                    case Role of
+                        server ->
+                            lists:member(?kex_strict_c,
+                                         Client#ssh_msg_kexinit.kex_algorithms);
+                        client ->
+                            lists:member(?kex_strict_s,
+                                         Server#ssh_msg_kexinit.kex_algorithms)
+                    end,
+                case Result of
+                    true ->
+                        logger:debug(lists:concat([Role, " will use strict KEX ordering"]));
+                    _ ->
+                        ok
+                end,
+                Result;
+            _ ->
+                KexStrictNegotiated0
+        end,
     {Encrypt0, Decrypt0} = select_encrypt_decrypt(Role, Client, Server),
     {SendMac0, RecvMac0} = select_send_recv_mac(Role, Client, Server),
 
@@ -1043,14 +1144,9 @@ select_algorithm(Role, Client, Server, O
 		   Server#ssh_msg_kexinit.languages_client_to_server),
     S_Lng = select(Client#ssh_msg_kexinit.languages_server_to_client,
 		   Server#ssh_msg_kexinit.languages_server_to_client),
-    HKey = select_all(Client#ssh_msg_kexinit.server_host_key_algorithms,
-		      Server#ssh_msg_kexinit.server_host_key_algorithms),
-    HK = case HKey of
-	     [] -> undefined;
-	     [HK0|_] -> HK0
-	 end,
-    %% Fixme verify Kex against HKey list and algorithms
-    
+    HKey = select(Client#ssh_msg_kexinit.server_host_key_algorithms,
+                  Server#ssh_msg_kexinit.server_host_key_algorithms),
+    %% FIXME verify Kex against HKey list and algorithms (see RFC4253 sec 7.1)
     Kex = select(Client#ssh_msg_kexinit.kex_algorithms,
 		 Server#ssh_msg_kexinit.kex_algorithms),
 
@@ -1070,7 +1166,7 @@ select_algorithm(Role, Client, Server, O
         ?GET_OPT(recv_ext_info,Opts),
 
     {ok, #alg{kex = Kex,
-              hkey = HK,
+              hkey = HKey,
               encrypt = Encrypt,
               decrypt = Decrypt,
               send_mac = SendMac,
@@ -1080,7 +1176,8 @@ select_algorithm(Role, Client, Server, O
               c_lng = C_Lng,
               s_lng = S_Lng,
               send_ext_info = SendExtInfo,
-              recv_ext_info = RecvExtInfo
+              recv_ext_info = RecvExtInfo,
+              kex_strict_negotiated = KexStrictNegotiated
              }}.
 
 
@@ -1178,7 +1275,8 @@ alg_setup(snd, SSH) ->
 	    c_lng = ALG#alg.c_lng,
 	    s_lng = ALG#alg.s_lng,
             send_ext_info = ALG#alg.send_ext_info,
-            recv_ext_info = ALG#alg.recv_ext_info
+            recv_ext_info = ALG#alg.recv_ext_info,
+            kex_strict_negotiated = ALG#alg.kex_strict_negotiated
 	   };
 
 alg_setup(rcv, SSH) ->
@@ -1190,22 +1288,23 @@ alg_setup(rcv, SSH) ->
 	    c_lng = ALG#alg.c_lng,
 	    s_lng = ALG#alg.s_lng,
             send_ext_info = ALG#alg.send_ext_info,
-            recv_ext_info = ALG#alg.recv_ext_info
+            recv_ext_info = ALG#alg.recv_ext_info,
+            kex_strict_negotiated = ALG#alg.kex_strict_negotiated
 	   }.
 
-
-alg_init(snd, SSH0) ->
+alg_init(Dir = snd, SSH0) ->
     {ok,SSH1} = send_mac_init(SSH0),
     {ok,SSH2} = encrypt_init(SSH1),
     {ok,SSH3} = compress_init(SSH2),
-    SSH3;
+    {ok,SSH4} = maybe_reset_sequence(Dir, SSH3),
+    SSH4;
 
-alg_init(rcv, SSH0) ->
+alg_init(Dir = rcv, SSH0) ->
     {ok,SSH1} = recv_mac_init(SSH0),
     {ok,SSH2} = decrypt_init(SSH1),
     {ok,SSH3} = decompress_init(SSH2),
-    SSH3.
-
+    {ok,SSH4} = maybe_reset_sequence(Dir, SSH3),
+    SSH4.
 
 alg_final(snd, SSH0) ->
     {ok,SSH1} = send_mac_final(SSH0),
@@ -1219,38 +1318,27 @@ alg_final(rcv, SSH0) ->
     {ok,SSH3} = decompress_final(SSH2),
     SSH3.
 
-
-select_all(CL, SL) when length(CL) + length(SL) < ?MAX_NUM_ALGORITHMS ->
-    %% algortihms only used by client
-    %% NOTE: an algorithm occuring more than once in CL will still be present
-    %%       in CLonly. This is not a problem for nice clients.
-    CLonly = CL -- SL,
-
-    %% algorithms used by client and server (client pref)
-    lists:foldr(fun(ALG, Acc) -> 
-                      try [list_to_existing_atom(ALG) | Acc]
-                      catch
-                          %% If an malicious client uses the same non-existing algorithm twice,
-                          %% we will end up here
-                          _:_ -> Acc
-                      end
-              end, [], (CL -- CLonly));
-
-select_all(CL, SL) ->
-    Error = lists:concat(["Received too many algorithms (",length(CL),"+",length(SL)," >= ",?MAX_NUM_ALGORITHMS,")."]),
-    ?DISCONNECT(?SSH_DISCONNECT_PROTOCOL_ERROR,
-                Error).
-
-
 select([], []) ->
     none;
 select(CL, SL) ->
-    C = case select_all(CL,SL) of
-	    [] -> undefined;
-	    [ALG|_] -> ALG
-	end,
-    C.
-	    
+    select_first(CL, SL).
+
+select_first([ClientAlg | ClientRest], SL) ->
+    case lists:member(ClientAlg, SL) of
+        true ->
+            try list_to_existing_atom(ClientAlg) of
+                Alg when is_atom(Alg) ->
+                    Alg
+            catch
+                error:badarg ->
+                    select_first(ClientRest, SL)
+            end;
+        false ->
+            select_first(ClientRest, SL)
+    end;
+select_first([], _) ->
+    undefined.
+
 ssh_packet(#ssh_msg_kexinit{} = Msg, Ssh0) ->
     BinMsg = ssh_message:encode(Msg),
     Ssh = key_init(Ssh0#ssh.role, Ssh0, BinMsg),
@@ -1282,9 +1370,9 @@ pack(common, rfc4253, PlainText, DeltaLe
      #ssh{send_sequence = SeqNum,
           send_mac = MacAlg,
           send_mac_key = MacKey} = Ssh0) ->
-    PadLen = padding_length(4+1+size(PlainText), Ssh0),
+    PadLen = padding_length(4+1+byte_size(PlainText), Ssh0),
     Pad =  ssh_bits:random(PadLen),
-    TextLen = 1 + size(PlainText) + PadLen + DeltaLenTst,
+    TextLen = 1 + byte_size(PlainText) + PadLen + DeltaLenTst,
     PlainPkt = <<?UINT32(TextLen),?BYTE(PadLen), PlainText/binary, Pad/binary>>,
     {Ssh1, CipherPkt} = encrypt(Ssh0, PlainPkt),
     MAC0 = mac(MacAlg, MacKey, SeqNum, PlainPkt),
@@ -1295,9 +1383,9 @@ pack(common, enc_then_mac, PlainText, De
      #ssh{send_sequence = SeqNum,
           send_mac = MacAlg,
           send_mac_key = MacKey} = Ssh0) ->
-    PadLen = padding_length(1+size(PlainText), Ssh0),
+    PadLen = padding_length(1+byte_size(PlainText), Ssh0),
     Pad =  ssh_bits:random(PadLen),
-    PlainLen = 1 + size(PlainText) + PadLen + DeltaLenTst,
+    PlainLen = 1 + byte_size(PlainText) + PadLen + DeltaLenTst,
     PlainPkt = <<?BYTE(PadLen), PlainText/binary, Pad/binary>>,
     {Ssh1, CipherPkt} = encrypt(Ssh0, PlainPkt),
     EncPacketPkt = <<?UINT32(PlainLen), CipherPkt/binary>>,
@@ -1305,9 +1393,9 @@ pack(common, enc_then_mac, PlainText, De
     {<<?UINT32(PlainLen), CipherPkt/binary, MAC0/binary>>, Ssh1};
 
 pack(aead, _, PlainText, DeltaLenTst, Ssh0) ->
-    PadLen = padding_length(1+size(PlainText), Ssh0),
+    PadLen = padding_length(1+byte_size(PlainText), Ssh0),
     Pad =  ssh_bits:random(PadLen),
-    PlainLen = 1 + size(PlainText) + PadLen + DeltaLenTst,
+    PlainLen = 1 + byte_size(PlainText) + PadLen + DeltaLenTst,
     PlainPkt = <<?BYTE(PadLen), PlainText/binary, Pad/binary>>,
     {Ssh1, {CipherPkt,MAC0}} = encrypt(Ssh0, <<?UINT32(PlainLen),PlainPkt/binary>>),
     {<<CipherPkt/binary,MAC0/binary>>, Ssh1}.
@@ -1334,7 +1422,7 @@ handle_packet_part(<<>>, Encrypted0, AEA
     end;
 
 handle_packet_part(DecryptedPfx, EncryptedBuffer, AEAD, TotalNeeded, Ssh0) 
-  when (size(DecryptedPfx)+size(EncryptedBuffer)) < TotalNeeded ->
+  when (byte_size(DecryptedPfx)+byte_size(EncryptedBuffer)) < TotalNeeded ->
     %% need more bytes to finalize the packet
     {get_more, DecryptedPfx, EncryptedBuffer, AEAD, TotalNeeded, Ssh0};
 
@@ -1353,7 +1441,7 @@ handle_packet_part(DecryptedPfx, Encrypt
 %%%----------------
 unpack(common, rfc4253, DecryptedPfx, EncryptedBuffer, _AEAD, TotalNeeded,
        #ssh{recv_mac_size = MacSize} = Ssh0) ->
-    MoreNeeded = TotalNeeded - size(DecryptedPfx) - MacSize,
+    MoreNeeded = TotalNeeded - byte_size(DecryptedPfx) - MacSize,
     <<EncryptedSfx:MoreNeeded/binary, Mac:MacSize/binary, NextPacketBytes/binary>> = EncryptedBuffer,
     {Ssh1, DecryptedSfx} = decrypt(Ssh0, EncryptedSfx),
     PlainPkt = <<DecryptedPfx/binary, DecryptedSfx/binary>>,
@@ -1370,7 +1458,7 @@ unpack(common, enc_then_mac, <<?UINT32(P
     case is_valid_mac(MAC0, <<?UINT32(PlainLen),Payload/binary>>, Ssh0) of
         true ->
             {Ssh1, <<?BYTE(PaddingLen), PlainRest/binary>>} = decrypt(Ssh0, Payload),
-            CompressedPlainTextLen = size(PlainRest) - PaddingLen,
+            CompressedPlainTextLen = byte_size(PlainRest) - PaddingLen,
             <<CompressedPlainText:CompressedPlainTextLen/binary, _Padding/binary>> = PlainRest,
             {ok, CompressedPlainText, NextPacketBytes, Ssh1};
         false ->
@@ -1380,7 +1468,7 @@ unpack(common, enc_then_mac, <<?UINT32(P
 unpack(aead, _, DecryptedPfx, EncryptedBuffer, AEAD, TotalNeeded, 
        #ssh{recv_mac_size = MacSize} = Ssh0) ->
     %% enough bytes to decode the packet.
-    MoreNeeded = TotalNeeded - size(DecryptedPfx) - MacSize,
+    MoreNeeded = TotalNeeded - byte_size(DecryptedPfx) - MacSize,
     <<EncryptedSfx:MoreNeeded/binary, Mac:MacSize/binary, NextPacketBytes/binary>> = EncryptedBuffer,
     case decrypt(Ssh0, {AEAD,EncryptedSfx,Mac}) of
         {Ssh1, error} ->
@@ -1392,7 +1480,7 @@ unpack(aead, _, DecryptedPfx, EncryptedB
 
 %%%----------------------------------------------------------------
 get_length(common, rfc4253, EncryptedBuffer, #ssh{decrypt_block_size = BlockSize} = Ssh0) ->
-    case size(EncryptedBuffer) >= erlang:max(8, BlockSize) of
+    case byte_size(EncryptedBuffer) >= erlang:max(8, BlockSize) of
 	true ->
 	    <<EncBlock:BlockSize/binary, EncryptedRest/binary>> = EncryptedBuffer,
 	    {Ssh, 
@@ -1412,7 +1500,7 @@ get_length(common, enc_then_mac, Encrypt
     end;
 
 get_length(aead, _, EncryptedBuffer, Ssh) ->
-    case {size(EncryptedBuffer) >= 4, Ssh#ssh.decrypt} of
+    case {byte_size(EncryptedBuffer) >= 4, Ssh#ssh.decrypt} of
        {true, 'chacha20-poly1305@openssh.com'} ->
             <<EncryptedLen:4/binary, EncryptedRest/binary>> = EncryptedBuffer,
             {Ssh1,  PacketLenBin} = decrypt(Ssh, {length,EncryptedLen}),
@@ -1446,12 +1534,28 @@ payload(<<PacketLen:32, PaddingLen:8, Pa
     Payload.
 
 %%%----------------------------------------------------------------
+%% sign(SigData, SignAlg, Key, Opts) when is_list(SignAlg) ->
+%%     sign(SigData, list_to_existing_atom(SignAlg), Key, Opts);
+
+sign(SigData, SignAlg, Key, #ssh{opts=Opts}) when is_atom(SignAlg) ->
+    case lists:member(SignAlg,
+                      proplists:get_value(public_key,
+                                          ?GET_OPT(preferred_algorithms,Opts,[]))) of
+        true ->
+            {ok, sign(SigData, sha(SignAlg), Key)};
+        false ->
+            {error, unsupported_sign_alg}
+    end.
+
 sign(SigData, HashAlg, #{algorithm:=dss} = Key) ->
     mk_dss_sig(crypto:sign(dss, HashAlg, SigData, Key));
 sign(SigData, HashAlg, #{algorithm:=SigAlg} = Key) ->
     crypto:sign(SigAlg, HashAlg, SigData, Key);
 sign(SigData, HashAlg,  #'DSAPrivateKey'{} = Key) ->
     mk_dss_sig(public_key:sign(SigData, HashAlg, Key));
+sign(SigData, HashAlg, Key = #'ECPrivateKey'{parameters = {namedCurve, Curve}})
+  when (Curve == ?'id-Ed25519') orelse (Curve == ?'id-Ed448') ->
+    public_key:sign(SigData, HashAlg, Key);
 sign(SigData, HashAlg, Key = #'ECPrivateKey'{}) ->
     DerEncodedSign =  public_key:sign(SigData, HashAlg, Key),
     #'ECDSA-Sig-Value'{r=R, s=S} = public_key:der_decode('ECDSA-Sig-Value', DerEncodedSign),
@@ -1465,7 +1569,11 @@ mk_dss_sig(DerSignature) ->
     <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>.
 
 %%%----------------------------------------------------------------
-verify(PlainText, HashAlg, Sig, {_,  #'Dss-Parms'{}} = Key, _) ->
+verify(PlainText, Alg, Sig, Key, Ssh) ->
+    do_verify(PlainText, sha(Alg), Sig, Key, Ssh).
+
+
+do_verify(PlainText, HashAlg, Sig, {_,  #'Dss-Parms'{}} = Key, _) ->
     case Sig of
         <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> ->
             Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}),
@@ -1473,7 +1581,7 @@ verify(PlainText, HashAlg, Sig, {_,  #'D
         _ ->
             false
     end;
-verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key, _) ->
+do_verify(PlainText, HashAlg, Sig, {#'ECPoint'{},_} = Key, _) when HashAlg =/= undefined ->
     case Sig of
         <<?UINT32(Rlen),R:Rlen/big-signed-integer-unit:8,
           ?UINT32(Slen),S:Slen/big-signed-integer-unit:8>> ->
@@ -1484,14 +1592,14 @@ verify(PlainText, HashAlg, Sig, {#'ECPoi
             false
     end;
 
-verify(PlainText, HashAlg, Sig, #'RSAPublicKey'{}=Key, #ssh{role = server,
-                                                            c_version = "SSH-2.0-OpenSSH_7."++_})
+do_verify(PlainText, HashAlg, Sig, #'RSAPublicKey'{}=Key, #ssh{role = server,
+                                                               c_version = "SSH-2.0-OpenSSH_7."++_})
   when HashAlg == sha256; HashAlg == sha512 ->
     %% Public key signing bug in in OpenSSH >= 7.2
     public_key:verify(PlainText, HashAlg, Sig, Key)
         orelse public_key:verify(PlainText, sha, Sig, Key);
 
-verify(PlainText, HashAlg, Sig, Key, _) ->
+do_verify(PlainText, HashAlg, Sig, Key, _) ->
     public_key:verify(PlainText, HashAlg, Sig, Key).
 
 
@@ -1741,7 +1849,7 @@ decrypt(#ssh{decrypt = 'chacha20-poly130
             %% The length is already decrypted and used to divide the input
             %% Check the mac (important that it is timing-safe):
             PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false),
-            case crypto:equal_const_time(Ctag, crypto:mac(poly1305, PolyKey, <<AAD/binary,Ctext/binary>>)) of
+            case ssh_lib:comp(Ctag, crypto:mac(poly1305, PolyKey, <<AAD/binary,Ctext/binary>>)) of
                 true ->
                     %% MAC is ok, decode
                     IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
@@ -1988,38 +2096,27 @@ valid_key_sha_alg(private, #'RSAPrivateK
 valid_key_sha_alg(public, {_, #'Dss-Parms'{}}, 'ssh-dss') -> true;
 valid_key_sha_alg(private, #'DSAPrivateKey'{},  'ssh-dss') -> true;
 
-valid_key_sha_alg(public, {ed_pub, ed25519,_},  'ssh-ed25519') -> true;
-valid_key_sha_alg(private, {ed_pri, ed25519,_,_},'ssh-ed25519') -> true;
-valid_key_sha_alg(public, {ed_pub, ed448,_},    'ssh-ed448') -> true;
-valid_key_sha_alg(private, {ed_pri, ed448,_,_},  'ssh-ed448') -> true;
-
-valid_key_sha_alg(public, {#'ECPoint'{},{namedCurve,OID}}, Alg) when is_tuple(OID) ->
+valid_key_sha_alg(public, {#'ECPoint'{},{namedCurve,OID}}, Alg) ->
     valid_key_sha_alg_ec(OID, Alg);
-valid_key_sha_alg(private, #'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) when is_tuple(OID) ->
+valid_key_sha_alg(private, #'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) ->
     valid_key_sha_alg_ec(OID, Alg);
 valid_key_sha_alg(_, _, _) -> false.
-    
-valid_key_sha_alg_ec(OID, Alg) ->
-    try
-        Curve = public_key:oid2ssh_curvename(OID),
-        Alg == list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(Curve))
-    catch
-        _:_ -> false
-    end.
+
+
+valid_key_sha_alg_ec(OID, Alg) when is_tuple(OID) ->
+    {SshCurveType, _} = ssh_message:oid2ssh_curvename(OID),
+    Alg == binary_to_atom(SshCurveType);
+valid_key_sha_alg_ec(_, _) -> false.
+
     
 
 -dialyzer({no_match, public_algo/1}).
 
 public_algo(#'RSAPublicKey'{}) ->   'ssh-rsa';  % FIXME: Not right with draft-curdle-rsa-sha2
 public_algo({_, #'Dss-Parms'{}}) -> 'ssh-dss';
-public_algo({ed_pub, ed25519,_}) -> 'ssh-ed25519';
-public_algo({ed_pub, ed448,_}) -> 'ssh-ed448';
 public_algo({#'ECPoint'{},{namedCurve,OID}}) when is_tuple(OID) -> 
-    SshName = public_key:oid2ssh_curvename(OID),
-    try list_to_existing_atom("ecdsa-sha2-" ++ binary_to_list(SshName))
-    catch
-        _:_ -> undefined
-    end.
+    {SshCurveType, _} = ssh_message:oid2ssh_curvename(OID),
+    binary_to_atom(SshCurveType).
 
 
 sha('ssh-rsa') -> sha;
@@ -2104,10 +2201,10 @@ parallell_gen_key(Ssh = #ssh{keyex_key =
     Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}}.
 
 
-generate_key(ecdh = Algorithm, Args) ->
-    crypto:generate_key(Algorithm, Args);
-generate_key(Algorithm, Args) ->
-    {Public,Private} = crypto:generate_key(Algorithm, Args),
+generate_key(ecdh, Args) ->
+    crypto:generate_key(ecdh, Args);
+generate_key(dh, [P,G,Sz2]) ->
+    {Public,Private} = crypto:generate_key(dh, [P, G, max(Sz2,?MIN_DH_KEY_SIZE)] ),
     {crypto:bytes_to_integer(Public), crypto:bytes_to_integer(Private)}.
 
 
@@ -2161,6 +2258,14 @@ crypto_name_supported(Tag, CryptoName, S
 
 same(Algs) ->  [{client2server,Algs}, {server2client,Algs}].
 
+maybe_reset_sequence(snd, Ssh = #ssh{kex_strict_negotiated = true}) ->
+    {ok, Ssh#ssh{send_sequence = 0}};
+maybe_reset_sequence(rcv, Ssh = #ssh{kex_strict_negotiated = true}) ->
+    {ok, Ssh#ssh{recv_sequence = 0}};
+maybe_reset_sequence(_Dir, Ssh) ->
+    {ok, Ssh}.
+
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %%
 %% Other utils
@@ -2187,14 +2292,14 @@ ssh_dbg_flags(raw_messages) -> ssh_dbg_f
 ssh_dbg_flags(ssh_messages) -> ssh_dbg_flags(hello).
 
 
-ssh_dbg_on(alg) -> dbg:tpl(?MODULE,select_algorithm,4,x);
+ssh_dbg_on(alg) -> dbg:tpl(?MODULE,select_algorithm,5,x);
 ssh_dbg_on(hello) -> dbg:tp(?MODULE,hello_version_msg,1,x),
                      dbg:tp(?MODULE,handle_hello_version,1,x);
 ssh_dbg_on(raw_messages) -> ssh_dbg_on(hello);
 ssh_dbg_on(ssh_messages) -> ssh_dbg_on(hello).
 
 
-ssh_dbg_off(alg) -> dbg:ctpl(?MODULE,select_algorithm,4);
+ssh_dbg_off(alg) -> dbg:ctpl(?MODULE,select_algorithm,5);
 ssh_dbg_off(hello) -> dbg:ctpg(?MODULE,hello_version_msg,1),
                       dbg:ctpg(?MODULE,handle_hello_version,1);
 ssh_dbg_off(raw_messages) -> ssh_dbg_off(hello);
@@ -2217,9 +2322,9 @@ ssh_dbg_format(hello, {call,{?MODULE,han
 ssh_dbg_format(hello, {return_from,{?MODULE,handle_hello_version,1},_Ret}) ->
     skip;
 
-ssh_dbg_format(alg, {call,{?MODULE,select_algorithm,[_,_,_,_]}}) ->
+ssh_dbg_format(alg, {call,{?MODULE,select_algorithm,[_,_,_,_,_]}}) ->
     skip;
-ssh_dbg_format(alg, {return_from,{?MODULE,select_algorithm,4},{ok,Alg}}) ->
+ssh_dbg_format(alg, {return_from,{?MODULE,select_algorithm,5},{ok,Alg}}) ->
     ["Negotiated algorithms:\n",
      wr_record(Alg)
     ];
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_xfer.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_xfer.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_xfer.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -242,7 +242,7 @@ xf_request(XF, Op, Arg) ->
 	       is_list(Arg) ->
 		   ?to_binary(Arg)
 	   end,
-    Size = 1+size(Data),
+    Size = 1+byte_size(Data),
     ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]).
 
 xf_send_reply(#ssh_xfer{cm = CM, channel = Channel}, Op, Arg) ->    
@@ -252,7 +252,7 @@ xf_send_reply(#ssh_xfer{cm = CM, channel
 	       is_list(Arg) ->
 		   ?to_binary(Arg)
 	   end,
-    Size = 1 + size(Data),
+    Size = 1 + byte_size(Data),
     ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]).
 
 xf_send_name(XF, ReqId, Name, Attr) ->
@@ -290,7 +290,7 @@ xf_send_status(#ssh_xfer{cm = CM, channe
     LangTag = "en",
     ELen = length(ErrorMsg),
     TLen = 2, %% length(LangTag),
-    Size = 1 + 4 + 4 + 4+ELen + 4+TLen + size(Data),
+    Size = 1 + 4 + 4 + 4+ELen + 4+TLen + byte_size(Data),
     ToSend = [<<?UINT32(Size), ?SSH_FXP_STATUS, ?UINT32(ReqId),
 	       ?UINT32(ErrorCode)>>,
 	      <<?UINT32(ELen)>>, ErrorMsg,
@@ -300,13 +300,13 @@ xf_send_status(#ssh_xfer{cm = CM, channe
 
 xf_send_attr(#ssh_xfer{cm = CM, channel = Channel, vsn = Vsn}, ReqId, Attr) ->
     EncAttr = encode_ATTR(Vsn, Attr),
-    ALen = size(EncAttr),
+    ALen = byte_size(EncAttr),
     Size = 1 + 4 + ALen,
     ToSend = [<<?UINT32(Size), ?SSH_FXP_ATTRS, ?UINT32(ReqId)>>, EncAttr],
     ssh_connection:send(CM, Channel, ToSend).
 
 xf_send_data(#ssh_xfer{cm = CM, channel = Channel}, ReqId, Data) ->
-    DLen = size(Data),
+    DLen = byte_size(Data),
     Size = 1 + 4 + 4+DLen,
     ToSend = [<<?UINT32(Size), ?SSH_FXP_DATA, ?UINT32(ReqId), ?UINT32(DLen)>>,
 	      Data],
@@ -660,7 +660,7 @@ encode_As(Vsn, [{AName, X}|As], Flags, A
 	     encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_UIDGID,
 		       [?uint32(X) | Acc]);
 	ownergroup when Vsn>=5 ->
-	    X1 = list_to_binary(integer_to_list(X)), % TODO: check owner and group
+	    X1 = integer_to_binary(X), % TODO: check owner and group
 	    encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_OWNERGROUP,
 		      [?binary(X1) | Acc]);
 	permissions ->
@@ -736,13 +736,11 @@ decode_As(Vsn, [{AName, AField}|As], R,
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
 	ownergroup when ?is_set(?SSH_FILEXFER_ATTR_OWNERGROUP, Flags),Vsn>=5 ->
 	    <<?UINT32(Len), Bin:Len/binary, Tail2/binary>> = Tail,
-	    X = binary_to_list(Bin),
+	    X = binary_to_integer(Bin),
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
-
 	permissions when ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS,Flags),Vsn>=5->
 	    <<?UINT32(X), Tail2/binary>> = Tail,
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
-
 	permissions when ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS,Flags),Vsn=<3->
 	    <<?UINT32(X), Tail2/binary>> = Tail,
 	    R1 = setelement(AField, R, X),
@@ -757,7 +755,6 @@ decode_As(Vsn, [{AName, AField}|As], R,
 		       _ -> unknown
 		   end,
 	    decode_As(Vsn, As, R1#ssh_xfer_attr { type=Type}, Flags, Tail2);
-
 	acmodtime when ?is_set(?SSH_FILEXFER_ATTR_ACMODTIME,Flags),Vsn=<3 ->
 	    <<?UINT32(X), Tail2/binary>> = Tail,
 	    decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2);
@@ -815,21 +812,21 @@ encode_name(Vsn, {{NameUC,LongNameUC},At
 	LongName = binary_to_list(unicode:characters_to_binary(LongNameUC)),
     LNLen = length(LongName),
 	EncAttr = encode_ATTR(Vsn, Attr),
-    ALen = size(EncAttr),
+    ALen = byte_size(EncAttr),
     NewLen = Len + NLen + LNLen + 4 + 4 + ALen,
     {[<<?UINT32(NLen)>>, Name, <<?UINT32(LNLen)>>, LongName, EncAttr], NewLen};
 encode_name(Vsn, {NameUC,Attr}, Len) when Vsn =< 3 ->
     Name = binary_to_list(unicode:characters_to_binary(NameUC)),
     NLen = length(Name),
     EncAttr = encode_ATTR(Vsn, Attr),
-    ALen = size(EncAttr),
+    ALen = byte_size(EncAttr),
     NewLen = Len + NLen*2 + 4 + 4 + ALen,
     {[<<?UINT32(NLen)>>, Name, <<?UINT32(NLen)>>, Name, EncAttr], NewLen};
 encode_name(Vsn, {NameUC,Attr}, Len) when Vsn >= 4 ->
     Name = binary_to_list(unicode:characters_to_binary(NameUC)),
     NLen = length(Name),
     EncAttr = encode_ATTR(Vsn, Attr),
-    ALen = size(EncAttr),
+    ALen = byte_size(EncAttr),
     {[<<?UINT32(NLen)>>, Name, EncAttr],
      Len + 4 + NLen + ALen}.
 
Index: otp-OTP-23.3.4.19/lib/ssh/test/.gitignore
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/.gitignore
@@ -0,0 +1,7 @@
+*COVER.html
+
+ssh_sftp_SUITE_data/test_data*
+
+property_test/ssh_eqc_client_server_dirs/system
+property_test/ssh_eqc_client_server_dirs/user
+
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_connection_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_connection_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_connection_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -21,11 +21,10 @@
 %%
 -module(ssh_connection_SUITE).
 
--include_lib("common_test/include/ct.hrl").
 -include("ssh_connect.hrl").
 -include("ssh_test_lib.hrl").
-
-
+-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/assert.hrl").
 
 -export([
          suite/0,
@@ -43,10 +42,24 @@
          big_cat/1,
          connect_sock_not_passive/1,
          connect_sock_not_tcp/1,
+         connect2_invalid_options/1,
+         connect_invalid_port/1,
+         connect_invalid_timeout_0/1,
+         connect_invalid_timeout_1/1,
+         connect_invalid_options/1,
+         connect3_invalid_port/1,
+         connect3_invalid_options/1,
+         connect3_invalid_timeout_0/1,
+         connect3_invalid_timeout_1/1,
+         connect3_invalid_both/1,
+         connect4_invalid_two_0/1,
+         connect4_invalid_two_1/1,
+         connect4_invalid_two_2/1,
+         connect4_invalid_three/1,
          connect_timeout/1,
          daemon_sock_not_passive/1,
          daemon_sock_not_tcp/1,
-         do_interrupted_send/3,
+         do_interrupted_send/4,
          do_simple_exec/1,
          encode_decode_pty_opts/1,
          exec_disabled/1,
@@ -69,6 +82,7 @@
          send_after_exit/1,
          simple_eval/1,
          simple_exec/1,
+         simple_exec_more_data/1,
          simple_exec_sock/1,
          simple_exec_two_socks/1,
          small_cat/1,
@@ -76,6 +90,8 @@
          start_exec_direct_fun1_read_write/1,
          start_exec_direct_fun1_read_write_advanced/1,
          start_shell/1,
+         new_shell_dumb_term/1,
+         new_shell_xterm_term/1,
          start_shell_pty/1,
          start_shell_exec/1,
          start_shell_exec_direct_fun/1,
@@ -83,6 +99,7 @@
          start_shell_exec_direct_fun1_error_type/1,
          start_shell_exec_direct_fun2/1,
          start_shell_exec_direct_fun3/1,
+         start_shell_exec_direct_fun_more_data/1,
          start_shell_exec_fun/1,
          start_shell_exec_fun2/1,
          start_shell_exec_fun3/1,
@@ -91,10 +108,12 @@
          start_shell_sock_exec_fun/1,
          start_subsystem_on_closed_channel/1,
          stop_listener/1,
+         trap_exit_connect/1,
+         trap_exit_daemon/1,
+         handler_down_before_open/1,
          ssh_exec_echo/2 % called as an MFA
         ]).
 
--define(SSH_DEFAULT_PORT, 22).
 -define(EXEC_TIMEOUT, 10000).
 
 %%--------------------------------------------------------------------
@@ -117,6 +136,10 @@ all() ->
      exec_disabled,
      exec_shell_disabled,
      start_shell,
+     new_shell_dumb_term,
+     new_shell_xterm_term,
+     trap_exit_connect,
+     trap_exit_daemon,
      start_shell_pty,
      start_shell_exec,
      start_shell_exec_fun,
@@ -125,6 +148,7 @@ all() ->
      start_shell_exec_direct_fun,
      start_shell_exec_direct_fun2,
      start_shell_exec_direct_fun3,
+     start_shell_exec_direct_fun_more_data,
      start_shell_exec_direct_fun1_error,
      start_shell_exec_direct_fun1_error_type,
      start_exec_direct_fun1_read_write,
@@ -134,6 +158,20 @@ all() ->
      start_shell_sock_daemon_exec_multi,
      encode_decode_pty_opts,
      connect_sock_not_tcp,
+     connect2_invalid_options,
+     connect_invalid_port,
+     connect_invalid_options,
+     connect_invalid_timeout_0,
+     connect_invalid_timeout_1,
+     connect3_invalid_port,
+     connect3_invalid_options,
+     connect3_invalid_timeout_0,
+     connect3_invalid_timeout_1,
+     connect3_invalid_both,
+     connect4_invalid_two_0,
+     connect4_invalid_two_1,
+     connect4_invalid_two_2,
+     connect4_invalid_three,
      connect_timeout,
      daemon_sock_not_tcp,
      gracefull_invalid_version,
@@ -144,13 +182,15 @@ all() ->
      stop_listener,
      no_sensitive_leak,
      start_subsystem_on_closed_channel,
-     max_channels_option
+     max_channels_option,
+     handler_down_before_open
     ].
 groups() ->
     [{openssh, [], payload() ++ ptty() ++ sock()}].
 
 payload() ->
     [simple_exec,
+     simple_exec_more_data,
      simple_exec_sock,
      simple_exec_two_socks,
      small_cat,
@@ -180,9 +220,9 @@ end_per_suite(_Config) ->
 
 %%--------------------------------------------------------------------
 init_per_group(openssh, Config) ->
-    case ssh_test_lib:gen_tcp_connect("localhost", 22, []) of
+    case ssh_test_lib:gen_tcp_connect(?SSH_DEFAULT_PORT, []) of
 	{error,econnrefused} ->
-	    {skip,"No openssh deamon (econnrefused)"};
+	    {skip,"No openssh daemon (econnrefused)"};
 	{ok, Socket} ->
 	    gen_tcp:close(Socket),
 	    ssh_test_lib:openssh_sanity_check(Config)
@@ -199,7 +239,7 @@ init_per_testcase(_TestCase, Config) ->
     %% end_per_testcase will be run!
     end_per_testcase(any, Config),
     ssh:start(),
-    Config.
+    ssh_test_lib:verify_sanity_check(Config).
 
 end_per_testcase(_TestCase, _Config) ->
     ssh:stop().
@@ -208,15 +248,20 @@ end_per_testcase(_TestCase, _Config) ->
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
 simple_exec(Config) when is_list(Config) ->
-    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
-							     {user_interaction, false}]),
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     do_simple_exec(ConnectionRef).
 
+simple_exec_more_data(Config) when is_list(Config) ->
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
+    %% more data received, SSH window adjust needs to be sent by client
+    do_simple_exec(ConnectionRef, 60000).
 %%--------------------------------------------------------------------
 simple_exec_sock(_Config) ->
-    {ok, Sock} = ssh_test_lib:gen_tcp_connect("localhost", ?SSH_DEFAULT_PORT, [{active,false}]),
-    {ok, ConnectionRef} = ssh:connect(Sock, [{silently_accept_hosts, true},
-					     {user_interaction, false}]),
+    {ok, Sock} = ssh_test_lib:gen_tcp_connect(?SSH_DEFAULT_PORT, [{active,false}]),
+    {ok, ConnectionRef} = ssh:connect(Sock, [{save_accepted_host, false},
+                                             {silently_accept_hosts, true},
+                                             {user_interaction, true}
+                                            ]),
     do_simple_exec(ConnectionRef).
 
 %%--------------------------------------------------------------------
@@ -225,9 +270,10 @@ simple_exec_two_socks(_Config) ->
     F = fun() ->
                 spawn_link(
                   fun() ->
-                          {ok, Sock} = ssh_test_lib:gen_tcp_connect("localhost", ?SSH_DEFAULT_PORT, [{active,false}]),
-                          {ok, ConnectionRef} = ssh:connect(Sock, [{silently_accept_hosts, true},
-                                                                   {user_interaction, false}]),
+                          {ok, Sock} = ssh_test_lib:gen_tcp_connect(?SSH_DEFAULT_PORT, [{active,false}]),
+                          {ok, ConnectionRef} = ssh:connect(Sock, [{save_accepted_host, false},
+                                                                   {silently_accept_hosts, true},
+                                                                   {user_interaction, true}]),
                           Parent ! {self(),do_simple_exec(ConnectionRef)}
                   end)
         end,
@@ -241,9 +287,144 @@ simple_exec_two_socks(_Config) ->
     end.
 
 %%--------------------------------------------------------------------
+connect2_invalid_options(_Config) ->
+    {error, invalid_options} = ssh:connect(bogus_socket, {bad, option}).
+
+connect3_invalid_port(_Config) ->
+    {error, invalid_port} = ssh:connect(bogus_host, noport, [{key, value}]).
+
+connect3_invalid_options(_Config) ->
+    {error, invalid_options} = ssh:connect(bogus_host, 1337, bad_options).
+
+connect3_invalid_timeout_0(_Config) ->
+    {error, invalid_timeout} =
+        ssh:connect(bogus_socket, [{key, value}], short).
+
+connect3_invalid_timeout_1(_Config) ->
+    {error, invalid_timeout} =
+        ssh:connect(bogus_socket, [{key, value}], -1).
+
+connect3_invalid_both(_Config) ->
+    %% The actual reason is implementation dependent.
+    {error, _Reason} =
+        ssh:connect(bogus, no_list_or_port, no_list_or_timeout).
+
+connect_invalid_port(Config) ->
+    {Pid, Host, _Port, UserDir} = daemon_start(Config),
+
+    {error, invalid_port} =
+        ssh:connect(Host, undefined,
+                    [{silently_accept_hosts, true},
+                     {user, "foo"},
+                     {password, "morot"},
+                     {user_interaction, false},
+                     {user_dir, UserDir}],
+                    infinity),
+
+    ssh:stop_daemon(Pid).
+
+connect_invalid_timeout_0(Config) ->
+    {Pid, Host, Port, UserDir} = daemon_start(Config),
+
+    {error, invalid_timeout} =
+        ssh:connect(Host, Port,
+                    [{silently_accept_hosts, true},
+                     {user, "foo"},
+                     {password, "morot"},
+                     {user_interaction, false},
+                     {user_dir, UserDir}],
+                   longer),
+
+    ssh:stop_daemon(Pid).
+
+connect_invalid_timeout_1(Config) ->
+    {Pid, Host, Port, UserDir} = daemon_start(Config),
+
+    {error, invalid_timeout} =
+        ssh:connect(Host, Port,
+                    [{silently_accept_hosts, true},
+                     {user, "foo"},
+                     {password, "morot"},
+                     {user_interaction, false},
+                     {user_dir, UserDir}],
+                   -1),
+
+    ssh:stop_daemon(Pid).
+
+connect_invalid_options(Config) ->
+    {Pid, Host, Port, _UserDir} = daemon_start(Config),
+
+    {error, invalid_options} =
+        ssh:connect(Host, Port,
+                    {user, "foo"},
+                    infinity),
+
+    ssh:stop_daemon(Pid).
+
+%% Two out three arguments incorrect.  Three possibilities.
+connect4_invalid_two_0(Config) ->
+    {Pid, Host, _Port, _UserDir} = daemon_start(Config),
+
+    %% Actual error implementation dependent
+    {error, _} =
+        ssh:connect(Host, noport,
+                    {user, "foo"},
+                    infinity),
+
+    ssh:stop_daemon(Pid).
+
+connect4_invalid_two_1(Config) ->
+    {Pid, Host, Port, _UserDir} = daemon_start(Config),
+
+    %% Actual error implementation dependent
+    {error, _} =
+        ssh:connect(Host, Port,
+                    {user, "foo"},
+                    short),
+
+    ssh:stop_daemon(Pid).
+
+connect4_invalid_two_2(Config) ->
+    {Pid, Host, _Port, _UserDir} = daemon_start(Config),
+
+    %% Actual error implementation dependent
+    {error, _} =
+        ssh:connect(Host, newport,
+                    [{user, "foo"}],
+                    -1),
+
+    ssh:stop_daemon(Pid).
+
+%% All three args incorrect
+connect4_invalid_three(Config) ->
+    {Pid, Host, _Port, _UserDir} = daemon_start(Config),
+
+    %% Actual error implementation dependent
+    {error, _} =
+        ssh:connect(Host, teleport,
+                    {user, "foo"},
+                    fortnight),
+
+    ssh:stop_daemon(Pid).
+
+daemon_start(Config) ->
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+
+    {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+                                             {user_dir, UserDir},
+                                             {password, "morot"},
+                                             {exec, fun ssh_exec_echo/1}]),
+    {Pid, Host, Port, UserDir}.
+
+%%--------------------------------------------------------------------
 connect_sock_not_tcp(_Config) ->
     {ok,Sock} = gen_udp:open(0, []), 
-    {error, not_tcp_socket} = ssh:connect(Sock, []),
+    {error, not_tcp_socket} = ssh:connect(Sock, [{save_accepted_host, false},
+                                                 {silently_accept_hosts, true},
+                                                 {user_interaction, true}]),
     gen_udp:close(Sock).
 
 %%--------------------------------------------------------------------
@@ -263,21 +444,21 @@ daemon_sock_not_tcp(_Config) ->
 
 %%--------------------------------------------------------------------
 connect_sock_not_passive(_Config) ->
-    {ok,Sock} = ssh_test_lib:gen_tcp_connect("localhost", ?SSH_DEFAULT_PORT, []), 
+    {ok,Sock} = ssh_test_lib:gen_tcp_connect(?SSH_DEFAULT_PORT, []), 
     {error, not_passive_mode} = ssh:connect(Sock, [{save_accepted_host, false},
-                                                   {silently_accept_hosts, true}]),
+                                                   {silently_accept_hosts, true},
+                                                   {user_interaction, true}]),
     gen_tcp:close(Sock).
 
 %%--------------------------------------------------------------------
 daemon_sock_not_passive(_Config) ->
-    {ok,Sock} = ssh_test_lib:gen_tcp_connect("localhost", ?SSH_DEFAULT_PORT, []), 
+    {ok,Sock} = ssh_test_lib:gen_tcp_connect(?SSH_DEFAULT_PORT, []), 
     {error, not_passive_mode} = ssh:daemon(Sock),
     gen_tcp:close(Sock).
 
 %%--------------------------------------------------------------------
 small_cat(Config) when is_list(Config) ->
-    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
-							     {user_interaction, false}]),
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
     success = ssh_connection:exec(ConnectionRef, ChannelId0,
 				  "cat", infinity),
@@ -365,8 +546,7 @@ big_cat(Config) when is_list(Config) ->
 
 %%--------------------------------------------------------------------
 send_after_exit(Config) when is_list(Config) ->
-    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
-							     {user_interaction, false}]),
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
     Data = <<"I like spaghetti squash">>,
 
@@ -430,8 +610,7 @@ encode_decode_pty_opts(_Config) ->
 
 %%--------------------------------------------------------------------
 ptty_alloc_default(Config) when is_list(Config) ->
-    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
-							     {user_interaction, false}]),
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
     Expect = case proplists:get_value(ptty_supported, Config) of
                  true -> success;
@@ -442,8 +621,7 @@ ptty_alloc_default(Config) when is_list(
 
 %%--------------------------------------------------------------------
 ptty_alloc(Config) when is_list(Config) ->
-    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
-							     {user_interaction, false}]),
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
     Expect = case proplists:get_value(ptty_supported, Config) of
                  true -> success;
@@ -456,8 +634,7 @@ ptty_alloc(Config) when is_list(Config)
 
 %%--------------------------------------------------------------------
 ptty_alloc_pixel(Config) when is_list(Config) ->
-    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
-							     {user_interaction, false}]),
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
     Expect = case proplists:get_value(ptty_supported, Config) of
                  true -> success;
@@ -468,15 +645,24 @@ ptty_alloc_pixel(Config) when is_list(Co
     ssh:close(ConnectionRef).
 
 %%--------------------------------------------------------------------
-small_interrupted_send(Config) -> 
+%%- small_interrupted_send is interrupted by ssh_echo_server which is
+%%  done with transferring data towards client and terminates the
+%%  channel (this results with {error, closed} return value from
+%%  ssh_connection:send on the client side)
+%%- interrupted_send is interrupted when ssh_echo_server ran
+%%  out of ssh data window and closed channel
+small_interrupted_send(Config) ->
     K = 1024,
-    M = K*K,
-    do_interrupted_send(Config, 10*M, 4*K).
+    SendSize = 10 * K * K,
+    EchoSize = 4 * K,
+    do_interrupted_send(Config, SendSize, EchoSize, {error, closed}).
 interrupted_send(Config) ->
-    M = 1024*1024,
-    do_interrupted_send(Config, 10*M, 4*M).
+    K = 1024,
+    SendSize = 10 * K * K,
+    EchoSize = 4 * K * K,
+    do_interrupted_send(Config, SendSize, EchoSize, ok).
 
-do_interrupted_send(Config, SendSize, EchoSize) ->
+do_interrupted_send(Config, SendSize, EchoSize, SenderResult) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
     file:make_dir(UserDir),
@@ -486,7 +672,6 @@ do_interrupted_send(Config, SendSize, Ec
 					     {user_dir, UserDir},
 					     {password, "morot"},
 					     {subsystems, [{"echo_n",EchoSS_spec}]}]),
-    
     ct:log("~p:~p connect", [?MODULE,?LINE]),
     ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
 						      {user, "foo"},
@@ -494,32 +679,28 @@ do_interrupted_send(Config, SendSize, Ec
 						      {user_interaction, false},
 						      {user_dir, UserDir}]),
     ct:log("~p:~p connected", [?MODULE,?LINE]),
-
     %% build big binary
     Data = << <<X:32>> || X <- lists:seq(1,SendSize div 4)>>,
-
     %% expect remote end to send us EchoSize back
     <<ExpectedData:EchoSize/binary, _/binary>> = Data,
-
     %% Spawn listener. Otherwise we could get a deadlock due to filled buffers
     Parent = self(),
     ResultPid = spawn(
 		  fun() ->
 			  ct:log("~p:~p open channel",[?MODULE,?LINE]),
 			  {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
-			  ct:log("~p:~p start subsystem", [?MODULE,?LINE]),
+			  ct:log("~p:~p start ssh subsystem", [?MODULE,?LINE]),
 			  case ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity) of
 			      success ->
 				  Parent ! {self(), channelId, ChannelId},
-				  
-				  Result = 
+				  Result =
 				      try collect_data(ConnectionRef, ChannelId, EchoSize)
 				      of
-					  ExpectedData -> 
+					  ExpectedData ->
 					      ct:log("~p:~p got expected data",[?MODULE,?LINE]),
 					      ok;
 					  Other ->
-					      ct:log("~p:~p unexpect: ~p", [?MODULE,?LINE,Other]),
+					      ct:log("~p:~p unexpected: ~p", [?MODULE,?LINE,Other]),
 					      {fail,"unexpected result in listener"}
 				      catch
 					  Class:Exception ->
@@ -530,40 +711,40 @@ do_interrupted_send(Config, SendSize, Ec
 				  Parent ! {self(), channelId, error, Other}
 			  end
 		  end),
-    
     receive
 	{ResultPid, channelId, error, Other} ->
 	    ct:log("~p:~p channelId error ~p", [?MODULE,?LINE,Other]),
 	    ssh:close(ConnectionRef),
 	    ssh:stop_daemon(Pid),
 	    {fail, "ssh_connection:subsystem"};
-
 	{ResultPid, channelId, ChannelId} ->
 	    ct:log("~p:~p ~p going to send ~p bytes", [?MODULE,?LINE,self(),size(Data)]),
 	    SenderPid = spawn(fun() ->
 				      Parent ! {self(),  ssh_connection:send(ConnectionRef, ChannelId, Data, 30000)}
 			      end),
+            ct:log("SenderPid = ~p", [SenderPid]),
 	    receive
 	    	{ResultPid, result, {fail, Fail}} ->
 		    ct:log("~p:~p Listener failed: ~p", [?MODULE,?LINE,Fail]),
 		    {fail, Fail};
-
 		{ResultPid, result, Result} ->
 		    ct:log("~p:~p Got result: ~p", [?MODULE,?LINE,Result]),
 		    ssh:close(ConnectionRef),
 		    ssh:stop_daemon(Pid),
 		    ct:log("~p:~p Check sender", [?MODULE,?LINE]),
 		    receive
-			{SenderPid, {error, closed}} ->
-			    ct:log("~p:~p {error,closed} - That's what we expect :)",[?MODULE,?LINE]),
+			{SenderPid, SenderResult} ->
+			    ct:log("~p:~p ~p - That's what we expect :)",
+                                   [?MODULE,?LINE, SenderResult]),
 			    ok;
 			Msg ->
 			    ct:log("~p:~p Not expected send result: ~p",[?MODULE,?LINE,Msg]),
 			    {fail, "Not expected msg"}
 		    end;
-
 		{SenderPid, {error, closed}} ->
-		    ct:log("~p:~p {error,closed} - That's what we expect, but client channel handler has not reported yet",[?MODULE,?LINE]),
+		    ct:log("~p:~p ~p - That's what we expect, "
+                           "but client channel handler has not reported yet",
+                           [?MODULE,?LINE, SenderResult]),
 		    receive
 			{ResultPid, result, Result} ->
 			    ct:log("~p:~p Now got the result: ~p", [?MODULE,?LINE,Result]),
@@ -574,7 +755,6 @@ do_interrupted_send(Config, SendSize, Ec
 			    ct:log("~p:~p Got an unexpected msg ~p",[?MODULE,?LINE,Msg]),
 			    {fail, "Un-expected msg"}
 		    end;
-
 		Msg ->
 		    ct:log("~p:~p Got unexpected ~p",[?MODULE,?LINE,Msg]),
 		    {fail, "Unexpected msg"}
@@ -602,6 +782,76 @@ start_shell(Config) when is_list(Config)
     ssh:close(ConnectionRef),
     ssh:stop_daemon(Pid).
 
+%%--------------------------------------------------------------------
+new_shell_dumb_term(Config) when is_list(Config) ->
+    new_shell_helper(#{term => "dumb",
+                       cmds => ["one_atom_please.\n",
+                                "\^R" % attempt to trigger history search
+                               ],
+                       exp_output =>
+                           [<<"Enter command\r\n">>,
+                            <<"1> ">>,
+                            <<"one_atom_please.\r\n">>,
+                            <<"{simple_eval,one_atom_please}\r\n">>,
+                            <<"2> ">>],
+                       unexp_output =>
+                           [<<"\e[;1;4msearch:\e[0m ">>]},
+                    Config).
+
+%%--------------------------------------------------------------------
+new_shell_xterm_term(Config) when is_list(Config) ->
+    new_shell_helper(#{term => "xterm",
+                       cmds => ["one_atom_please.\n",
+                                "\^R" % attempt to trigger history search
+                               ],
+                       exp_output =>
+                           [<<"Enter command\r\n">>,
+                            <<"1> ">>,
+                            <<"one_atom_please.\r\n\e[1022D\e[1B">>,
+                            <<"{simple_eval,one_atom_please}\r\n">>,
+                            <<"2> ">>,
+                            <<"\e[3D\e[J">>,
+                            <<"\e[;1;4msearch:\e[0m ">>,
+                            <<"\r\n  one_atom_please.">>]},
+                    Config).
+
+new_shell_helper(#{term := Term, cmds := Cmds,
+                   exp_output := ExpectedOutput} = Settings, Config) ->
+    UnexpectedOutput = maps:get(unexp_output, Settings, []),
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+    {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+					     {user_dir, UserDir},
+					     {password, "morot"},
+                                             {subsystems, []},
+                                             {keepalive, true},
+                                             {nodelay, true},
+                                             {shell, fun(U, H) ->
+                                                             start_our_shell2(U, H)
+                                                     end}
+                                            ]),
+    ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+						      {user, "foo"},
+						      {password, "morot"},
+						      {user_dir, UserDir}]),
+    {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+    success =
+        ssh_connection:ptty_alloc(ConnectionRef, ChannelId,
+                                  [{term, Term}, {hight, 24}, {width,1023}],
+                                  infinity),
+    ok = ssh_connection:shell(ConnectionRef,ChannelId),
+    [ssh_connection:send(ConnectionRef, ChannelId, C) || C <- Cmds],
+    GetTuple = fun(Bin) -> {ssh_cm, ConnectionRef, {data,ChannelId,0,Bin}} end,
+    Msgs = [GetTuple(B) || B <- ExpectedOutput],
+    expected = ssh_test_lib:receive_exec_result(Msgs),
+    UnexpectedMsgs = [GetTuple(C) || C <- UnexpectedOutput],
+    flush_msgs(UnexpectedMsgs),
+
+    ssh:close(ConnectionRef),
+    ssh:stop_daemon(Pid).
+
 %%-------------------------------------------------------------------
 start_shell_pty(Config) when is_list(Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
@@ -773,6 +1023,24 @@ start_shell_exec_direct_fun3(Config) ->
                             "testing", <<"echo foo testing">>, 0,
                             Config).
 
+start_shell_exec_direct_fun_more_data(Config) ->
+    N = 60000,
+    ExpectedBin = <<"testing\n">>,
+    ReceiveFun =
+        fun(ConnectionRef, ChannelId, _Expect, _ExpectType) ->
+                receive_bytes(ConnectionRef, ChannelId,
+                              N * byte_size(ExpectedBin), 0)
+        end,
+    do_start_shell_exec_fun({direct,
+                             fun(_Cmd) ->
+                                     {ok,
+                                      [io_lib:format("testing~n",[]) ||
+                                          _ <- lists:seq(1, N)]}
+                             end},
+                            "not_relevant", <<"not_used\n">>, 0,
+                            ReceiveFun,
+                            Config).
+
 start_shell_exec_direct_fun1_error(Config) ->
     do_start_shell_exec_fun({direct, fun(_Cmd) -> {error, {bad}} end},
                             "testing", <<"**Error** {bad}">>, 1,
@@ -907,6 +1175,28 @@ simple_eval(Inp) -> {simple_eval,Inp}.
 
 
 do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, Config) ->
+    DefaultReceiveFun =
+        fun(ConnectionRef, ChannelId, _Expect, _ExpectType) ->
+                receive
+                    {ssh_cm, ConnectionRef, {data, ChannelId, ExpectType, Expect}} ->
+                        ok
+                after 5000 ->
+                        receive
+                            Other ->
+                                ct:log("Received other:~n~p~nExpected: ~p~n",
+                                       [Other,
+                                        {ssh_cm, ConnectionRef,
+                                         {data, ChannelId, ExpectType, Expect}}]),
+                                         %% {data, '_ChannelId', ExpectType, Expect}}]),
+                                ct:fail("Unexpected response")
+                        after 0 ->
+                                ct:fail("Exec Timeout")
+                        end
+                end
+        end,
+    do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, DefaultReceiveFun, Config).
+
+do_start_shell_exec_fun(Fun, Command, Expect, ExpectType, ReceiveFun, Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
     file:make_dir(UserDir),
@@ -922,26 +1212,64 @@ do_start_shell_exec_fun(Fun, Command, Ex
 						      {user_interaction, true},
 						      {user_dir, UserDir}]),
 
-    {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+    {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+    success = ssh_connection:exec(ConnectionRef, ChannelId, Command, infinity),
+    ReceiveFun(ConnectionRef, ChannelId, Expect, ExpectType),
+    ssh:close(ConnectionRef),
+    ssh:stop_daemon(Pid).
 
-    success = ssh_connection:exec(ConnectionRef, ChannelId0, Command, infinity),
+%%--------------------------------------------------------------------
+%% Issue GH-8223
+trap_exit_connect(Config) when is_list(Config) ->
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey),
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+    {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+                                             {user_dir, UserDir},
+                                             {password, "morot"}]),
+    %% Fake an EXIT message
+    ExitMsg = {'EXIT', self(), make_ref()},
+    self() ! ExitMsg,
 
+    {ok, ConnectionRef} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                                   {save_accepted_host, false},
+                                                   {user, "foo"},
+                                                   {password, "morot"},
+                                                   {user_interaction, true},
+                                                   {user_dir, UserDir}]),
+    ssh:close(ConnectionRef),
+    ssh:stop_daemon(Pid),
+
+    %% Ensure the EXIT message is still there
     receive
-	{ssh_cm, ConnectionRef, {data, _ChannelId, ExpectType, Expect}} ->
-	    ok
-    after 5000 ->
-            receive
-                Other ->
-                    ct:log("Received other:~n~p~nExpected: ~p~n",
-                           [Other, {ssh_cm, ConnectionRef, {data, '_ChannelId', ExpectType, Expect}} ]),
-                    ct:fail("Unexpected response")
-            after 0 ->
-                    ct:fail("Exec Timeout")
-            end
-    end,
+        ExitMsg -> ok
+    after 0 ->
+        ct:fail("No EXIT message")
+    end.
 
-    ssh:close(ConnectionRef),
-    ssh:stop_daemon(Pid).
+%%--------------------------------------------------------------------
+%% Issue GH-8223
+trap_exit_daemon(Config) when is_list(Config) ->
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey),
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+
+    %% Fake an EXIT message
+    ExitMsg = {'EXIT', self(), make_ref()},
+    self() ! ExitMsg,
+
+    {ok, DaemonRef} = ssh:daemon(0, [{system_dir, SysDir},
+                                     {user_dir, UserDir}]),
+    ssh:stop_daemon(DaemonRef),
+
+    %% Ensure the EXIT message is still there
+    receive
+        ExitMsg -> ok
+    after 0 ->
+        ct:fail("No EXIT message")
+    end.
 
 %%--------------------------------------------------------------------
 start_shell_sock_exec_fun(Config) when is_list(Config) ->
@@ -957,6 +1285,7 @@ start_shell_sock_exec_fun(Config) when i
 
     {ok, Sock} = ssh_test_lib:gen_tcp_connect(Host, Port, [{active,false}]),
     {ok,ConnectionRef} = ssh:connect(Sock, [{silently_accept_hosts, true},
+                                            {save_accepted_host, false},
 					    {user, "foo"},
 					    {password, "morot"},
 					    {user_interaction, true},
@@ -990,7 +1319,7 @@ start_shell_sock_daemon_exec(Config) ->
 
     %% A server tcp-contects to the listening socket and starts an ssh daemon
     spawn_link(fun() ->
-		       {ok,Ss} = ssh_test_lib:gen_tcp_connect("localhost", Port, [{active,false}]),
+		       {ok,Ss} = ssh_test_lib:gen_tcp_connect(Port, [{active,false}]),
 		       {ok, _Pid} = ssh:daemon(Ss, [{system_dir, SysDir},
 						    {user_dir, UserDir},
 						    {password, "morot"},
@@ -1000,6 +1329,7 @@ start_shell_sock_daemon_exec(Config) ->
     %% The client accepts the tcp connection from the server and ssh-connects to it
     {ok,Sc} = gen_tcp:accept(Sl),
     {ok,ConnectionRef} = ssh:connect(Sc, [{silently_accept_hosts, true},
+                                          {save_accepted_host, false},
 					  {user, "foo"},
 					  {password, "morot"},
 					  {user_interaction, true},
@@ -1041,7 +1371,7 @@ start_shell_sock_daemon_exec_multi(Confi
     %% Servers tcp-contects to the listening socket and starts an ssh daemon
     Pids =
         [spawn_link(fun() ->
-                            {ok,Ss} = ssh_test_lib:gen_tcp_connect("localhost", Port, [{active,false}]),
+                            {ok,Ss} = ssh_test_lib:gen_tcp_connect(Port, [{active,false}]),
                             {ok, _Pid} = ssh:daemon(Ss, DaemonOpts)
                     end)
          || _ <- lists:seq(1,NumConcurent)],
@@ -1052,6 +1382,7 @@ start_shell_sock_daemon_exec_multi(Confi
         [begin
              {ok,Sc} = gen_tcp:accept(Sl),
              {ok,ConnectionRef} = ssh:connect(Sc, [{silently_accept_hosts, true},
+                                                   {save_accepted_host, false},
                                                    {user, "foo"},
                                                    {password, "morot"},
                                                    {user_interaction, true},
@@ -1073,7 +1404,7 @@ start_shell_sock_daemon_exec_multi(Confi
                             receive
                                 {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"echo testing\n">>}} ->
                                     Parent ! {answer_received,self()},
-                                    ct:log("~p:~p: recevied result on connection ~p", [?MODULE,?LINE,ConnectionRef])
+                                    ct:log("~p:~p: received result on connection ~p", [?MODULE,?LINE,ConnectionRef])
                             after 5000 ->
                                     ct:fail("Exec Timeout")
                             end
@@ -1182,6 +1513,8 @@ gracefull_invalid_long_start_no_nl(Confi
     end.
 
 kex_error(Config) ->
+    #{level := Level} = logger:get_primary_config(),
+    ok = logger:set_primary_config(level, debug),
     PrivDir = proplists:get_value(priv_dir, Config),
     UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
     file:make_dir(UserDir),
@@ -1202,6 +1535,10 @@ kex_error(Config) ->
                                    ok % Other msg
                            end,
                            self()),
+    Cleanup = fun() ->
+                      ok = logger:remove_handler(kex_error),
+                      ok = logger:set_primary_config(level, Level)
+              end,
     try
         ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
                                           {user, "foo"},
@@ -1219,7 +1556,7 @@ kex_error(Config) ->
             %% ok
             receive
                 {Ref, ErrMsgTxt} ->
-                    ok = logger:remove_handler(kex_error),
+                    Cleanup(),
                     ct:log("ErrMsgTxt = ~n~s", [ErrMsgTxt]),
                     Lines = lists:map(fun string:trim/1, string:tokens(ErrMsgTxt, "\n")),
                     OK = (lists:all(fun(S) -> lists:member(S,Lines) end,
@@ -1237,18 +1574,19 @@ kex_error(Config) ->
                             ct:fail("unexpected error text msg", [])
                     end
             after 20000 ->
-                    ok = logger:remove_handler(kex_error),
+                    Cleanup(),
                     ct:fail("timeout", [])
             end;
 
         error:{badmatch,{error,_}} ->
-            ok = logger:remove_handler(kex_error),
+            Cleanup(),
             ct:fail("unexpected error msg", [])
     end.
 
 stop_listener(Config) when is_list(Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
-    UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+    %% to make sure we don't use public-key-auth
+    UserDir = filename:join(PrivDir, nopubkey),
     file:make_dir(UserDir),
     SysDir = proplists:get_value(data_dir, Config),
 
@@ -1257,17 +1595,20 @@ stop_listener(Config) when is_list(Confi
 					      {password, "morot"},
 					      {exec, fun ssh_exec_echo/1}]),
 
-    ConnectionRef0 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
-						       {user, "foo"},
-						       {password, "morot"},
-						       {user_interaction, true},
-						       {user_dir, UserDir}]),
-
-    {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef0, infinity),
+    ConnectionRef0 = ssh_test_lib:connect(Host, Port,
+                                          [{silently_accept_hosts, true},
+                                           {user, "foo"},
+                                           {password, "morot"},
+                                           {user_interaction, false},
+                                           {user_dir, UserDir}]),
+    {ok, ChannelId0} =
+        ssh_connection:session_channel(ConnectionRef0, infinity),
 
     ssh:stop_listener(Host, Port),
 
     {error, _} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                          {save_accepted_host, false},
+                                          {save_accepted_host, false},
 					  {user, "foo"},
 					  {password, "morot"},
 					  {user_interaction, true},
@@ -1275,7 +1616,8 @@ stop_listener(Config) when is_list(Confi
     success = ssh_connection:exec(ConnectionRef0, ChannelId0,
 				  "testing", infinity),
     receive
-	{ssh_cm, ConnectionRef0, {data, ChannelId0, 0, <<"echo testing\n">>}} ->
+	{ssh_cm, ConnectionRef0,
+         {data, ChannelId0, 0, <<"echo testing\n">>}} ->
 	    ok
     after 5000 ->
 	    ct:fail("Exec Timeout")
@@ -1286,12 +1628,14 @@ stop_listener(Config) when is_list(Confi
                                     {password, "potatis"},
                                     {exec, fun ssh_exec_echo/1}]) of
 	{Pid1, Host, Port} ->
-	    ConnectionRef1 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
-							       {user, "foo"},
-							       {password, "potatis"},
-							       {user_interaction, true},
-							       {user_dir, UserDir}]),
+	    ConnectionRef1 =
+                ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+                                                  {user, "foo"},
+                                                  {password, "potatis"},
+                                                  {user_interaction, true},
+                                                  {user_dir, UserDir}]),
 	    {error, _} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                                  {save_accepted_host, false},
                                                   {user, "foo"},
                                                   {password, "morot"},
                                                   {user_interaction, true},
@@ -1333,7 +1677,7 @@ no_sensitive_leak(Config) ->
     %% Install the test handler:
     Hname = no_sensitive_leak,
     ok = ssh_log_h:add_fun(Hname,
-                           fun(#{msg := {report, #{report := Rep}}}, Pid) ->
+                           fun(#{msg := {report,#{report := Rep}}}, Pid) ->
                                    true = (erlang:process_info(Pid, status) =/= undefined), % remove handler if we are dead
                                    Pid ! {Ref,ssh_log_h:sensitive_in_opt(Rep),Rep};
                               (_,Pid) ->
@@ -1354,8 +1698,9 @@ no_sensitive_leak(Config) ->
 						       {user_dir, UserDir}]),
     %% Kill acceptor to make it restart:
     [true|_] =
-        [exit(Pacc,kill) || {{server,ssh_system_sup,_,_,default},P1,supervisor,_} <- supervisor:which_children(sshd_sup),
-                            {{ssh_acceptor_sup,_,_,default},Pacc,supervisor,_} <- supervisor:which_children(P1)],
+        [exit(Pacc,kill) || {{ssh_system_sup,_},P1,supervisor,_} <- supervisor:which_children(sshd_sup),
+                            {{ssh_acceptor_sup,_},P2,supervisor,_} <- supervisor:which_children(P1),
+                            {{ssh_acceptor_sup,_},Pacc,worker,_} <- supervisor:which_children(P2)],
     
     %% Remove the test handler and reset the logger level:
     timer:sleep(500),
@@ -1486,47 +1831,206 @@ max_channels_option(Config) when is_list
     ssh:close(ConnectionRef),
     ssh:stop_daemon(Pid).
 
+handler_down_before_open(Config) ->
+    %% Start echo subsystem with a delay in init() - until a signal is received
+    %% One client opens a channel on the connection
+    %% the other client requests the echo subsystem on the second channel and then immediately goes down
+    %% the test monitors the client and when receiving 'DOWN' signals 'echo' to proceed
+    %% a) there should be no crash after 'channel-open-confirmation'
+    %% b) there should be proper 'channel-close' exchange
+    %% c) the 'exec' channel should not be affected after the 'echo' channel goes down
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+    Parent = self(),
+    EchoSS_spec = {ssh_echo_server, [8, [{dbg, true}, {parent, Parent}]]},
+    {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+					     {user_dir, UserDir},
+					     {password, "morot"},
+					     {exec, fun ssh_exec_echo/1},
+					     {subsystems, [{"echo_n",EchoSS_spec}]}]),
+    ct:log("~p:~p connect", [?MODULE,?LINE]),
+    ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+						      {user, "foo"},
+						      {password, "morot"},
+						      {user_interaction, false},
+						      {user_dir, UserDir}]),
+    ct:log("~p:~p connected", [?MODULE,?LINE]),
+
+    ExecChannelPid =
+        spawn(
+          fun() ->
+                  {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+                  %% This is to get peer's connection handler PID ({conn_peer ...} below) and suspend it
+                  {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
+                  ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", infinity),
+                  ssh_connection:close(ConnectionRef, ChannelId1),
+                  receive
+                      {ssh_cm, ConnectionRef, {closed, 1}} -> ok
+                  end,
+
+                  Parent ! {self(), channelId, ChannelId0},
+                  Result = receive
+                               cmd ->
+                                   ct:log("~p:~p Channel ~p executing", [?MODULE, ?LINE, ChannelId0]),
+                                   success = ssh_connection:exec(ConnectionRef, ChannelId0, "testing", infinity),
+                                   Expect = <<"echo testing\n">>,
+                                   ExpSz = size(Expect),
+                                   receive
+                                       {ssh_cm, ConnectionRef, {data, ChannelId0, 0,
+                                                                <<Expect:ExpSz/binary, _/binary>>}} = R ->
+                                           ct:log("~p:~p Got expected ~p",[?MODULE,?LINE, R]),
+                                           ok;
+                                       Other ->
+                                           ct:log("~p:~p Got unexpected ~p~nExpect: ~p~n",
+                                                  [?MODULE,?LINE, Other, {ssh_cm, ConnectionRef,
+                                                                          {data, ChannelId0, 0, Expect}}]),
+                                           {fail, "Unexpected data"}
+                                   after 5000 ->
+                                           {fail, "Exec Timeout"}
+                                   end;
+                               stop -> {fail, "Stopped"}
+                           end,
+                  Parent ! {self(), Result}
+          end),
+    try
+        receive
+            {ExecChannelPid, channelId, ExId} ->
+                ct:log("~p:~p Channel that should stay: ~p pid ~p",
+                       [?MODULE, ?LINE, ExId, ExecChannelPid]),
+                %% This is sent by the echo subsystem as a reaction to channel1 above
+                ConnPeer = receive {conn_peer, CM} -> CM end,
+                %% The sole purpose of this channel is to go down
+                %% before the opening procedure is complete
+                DownChannelPid = spawn(
+                    fun() ->
+                        ct:log("~p:~p open channel (incomplete)",[?MODULE,?LINE]),
+                        Parent ! {self(), channelId, ok},
+                        %% This is to prevent the peer from answering our 'channel-open' in time
+                        sys:suspend(ConnPeer),
+                        {ok, _} = ssh_connection:session_channel(ConnectionRef, infinity)
+                    end),
+                MonRef = erlang:monitor(process, DownChannelPid),
+                receive
+                    {DownChannelPid, channelId, ok} ->
+                        ct:log("~p:~p Channel handler that won't continue: pid ~p",
+                               [?MODULE, ?LINE, DownChannelPid]),
+                        ensure_channels(ConnectionRef, 2),
+                        channel_down_sequence(DownChannelPid, ExecChannelPid,
+                                              ExId, MonRef, ConnectionRef, ConnPeer)
+                end
+        end,
+        ensure_channels(ConnectionRef, 0)
+    after
+        ssh:close(ConnectionRef),
+        ssh:stop_daemon(Pid)
+    end.
+
+ensure_channels(ConnRef, Expected) ->
+    {ok, ChannelList} = ssh_connection_handler:info(ConnRef),
+    do_ensure_channels(ConnRef, Expected, length(ChannelList)).
+
+do_ensure_channels(_ConnRef, NumExpected, NumExpected) ->
+    ok;
+do_ensure_channels(ConnRef, NumExpected, _ChannelListLen) ->
+    ct:sleep(100),
+    {ok, ChannelList} = ssh_connection_handler:info(ConnRef),
+    do_ensure_channels(ConnRef, NumExpected, length(ChannelList)).
+
+channel_down_sequence(DownChannelPid, ExecChannelPid, ExecChannelId, MonRef, ConnRef, Peer) ->
+    ct:log("~p:~p sending order to ~p to go down", [?MODULE, ?LINE, DownChannelPid]),
+    exit(DownChannelPid, die),
+    receive {'DOWN', MonRef, _, _, _} -> ok end,
+    ct:log("~p:~p order executed, sending order to ~p to proceed", [?MODULE, ?LINE, Peer]),
+    %% Resume the peer connection to let it clean up among its channels
+    sys:resume(Peer),
+    ensure_channels(ConnRef, 1),
+    ExecChannelPid ! cmd,
+    try
+        receive
+            {ExecChannelPid, ok} ->
+                ct:log("~p:~p expected exec result: ~p", [?MODULE, ?LINE, ok]),
+                ok;
+            {ExecChannelPid, Result} ->
+                ct:log("~p:~p Unexpected exec result: ~p", [?MODULE, ?LINE, Result]),
+                {fail, "Unexpected exec result"}
+        after 5000 ->
+            {fail, "Exec result timeout"}
+        end
+    after
+        ssh_connection:close(ConnRef, ExecChannelId)
+    end.
+
 %%--------------------------------------------------------------------
 %% Internal functions ------------------------------------------------
 %%--------------------------------------------------------------------
-
 do_simple_exec(ConnectionRef) ->
+    do_simple_exec(ConnectionRef, 1).
+
+do_simple_exec(ConnectionRef, N) ->
     {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
-    success = ssh_connection:exec(ConnectionRef, ChannelId0,
-				  "echo testing", infinity),
-    %% receive response to input
-    receive
-	{ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}} ->
-	    ok
-    after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+    Cmd = "yes testing | head -n " ++ integer_to_list(N),
+    ct:log("Cmd to be invoked over SSH shell: ~p", [Cmd]),
+    success = ssh_connection:exec(ConnectionRef, ChannelId0, Cmd, infinity),
+    ExpectedBin = <<"testing\n">>,
+    case N of
+        1 ->
+            %% receive response to input
+            receive
+                {ssh_cm, ConnectionRef, {data, ChannelId0, 0, ExpectedBin}} ->
+                    ok
+            after
+                10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+            end;
+        _ ->
+            receive_bytes(ConnectionRef, ChannelId0, N * byte_size(ExpectedBin), 0)
     end,
-
     %% receive close messages
-    receive
-	{ssh_cm, ConnectionRef, {eof, ChannelId0}} ->
-	    ok
+    CloseMessages =
+        [{ssh_cm, ConnectionRef, {eof, ChannelId0}},
+         {ssh_cm, ConnectionRef, {closed, ChannelId0}}],
+    Timeout = 10000,
+    [receive
+         M ->
+             ct:log("Received M = ~w", [M]),
+             ok
+     after
+         Timeout ->
+             ct:log("M = ~w not found !", [M]),
+             ct:log("Messages in queue =~n~p", [process_info(self(), messages)]),
+             ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+     end || M <- CloseMessages],
+    receive
+        %% 141 is exit status of `yes testing | head -n 1` on tcsh
+        %% other shells return 0
+        ExitMsg = {ssh_cm, ConnectionRef, {exit_status, ChannelId0, ExitStatus}}
+          when ExitStatus == 0; ExitStatus == 141 ->
+            ct:log("Received M = ~w", [ExitMsg]),
+            ok
     after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+        Timeout ->
+            ct:log("Acceptable exit status not received"),
+            ct:log("Messages in queue =~n~p", [process_info(self(), messages)]),
+            ct:fail("timeout ~p:~p",[?MODULE,?LINE])
     end,
-    receive
-	{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} ->
-	    ok
-    after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
-    end,
-    receive
-	{ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
-	    ok
-    after
-	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
-    end.
+    ok.
 
 
 %%--------------------------------------------------------------------
 flush_msgs() ->
+    flush_msgs([]).
+
+flush_msgs(Unexpected) ->
     receive
-        _ -> flush_msgs()
+        M ->
+            case lists:member(M, Unexpected) of
+                true ->
+                    ct:fail("Unexpected message found:  ~p", [M]);
+                _ ->
+                    flush_msgs()
+            end
     after
         500 -> ok
     end.
@@ -1614,6 +2118,7 @@ test_exec_is_enabled(ConnectionRef, Exec
         {ssh_cm, ConnectionRef, {data, ChannelId, 0, <<Expect:ExpSz/binary, _/binary>>}} = R ->
             ct:log("~p:~p Got expected ~p",[?MODULE,?LINE,R]);
         Other ->
+            %% FIXME - should this testcase fail when unexpected data is received?
             ct:log("~p:~p Got unexpected ~p~nExpect: ~p~n",
                    [?MODULE,?LINE, Other, {ssh_cm, ConnectionRef, {data, ChannelId, 0, Expect}} ])
     after 5000 ->
@@ -1627,8 +2132,6 @@ big_cat_rx(ConnectionRef, ChannelId) ->
 big_cat_rx(ConnectionRef, ChannelId, Acc) ->
     receive
 	{ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} ->
-	    %% ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
-	    %% window was pre-adjusted, don't adjust again here
 	    big_cat_rx(ConnectionRef, ChannelId, [Data | Acc]);
 	{ssh_cm, ConnectionRef, {eof, ChannelId}} ->
 	    {ok, iolist_to_binary(lists:reverse(Acc))}
@@ -1637,7 +2140,8 @@ big_cat_rx(ConnectionRef, ChannelId, Acc
     end.
 
 collect_data(ConnectionRef, ChannelId, EchoSize) ->
-    ct:log("~p:~p Listener ~p running! ConnectionRef=~p, ChannelId=~p",[?MODULE,?LINE,self(),ConnectionRef,ChannelId]),
+    ct:log("~p:~p Listener ~p running! ConnectionRef=~p, ChannelId=~p",
+           [?MODULE,?LINE,self(),ConnectionRef,ChannelId]),
     collect_data(ConnectionRef, ChannelId, EchoSize, [], 0).
 
 collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum) ->
@@ -1646,18 +2150,15 @@ collect_data(ConnectionRef, ChannelId, E
 	{ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} when is_binary(Data) ->
 	    ct:log("~p:~p collect_data: received ~p bytes. total ~p bytes,  want ~p more",
 		   [?MODULE,?LINE,size(Data),Sum+size(Data),EchoSize-Sum]),
-	    ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
+            ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)),
 	    collect_data(ConnectionRef, ChannelId, EchoSize, [Data | Acc], Sum+size(Data));
 	{ssh_cm, ConnectionRef, Msg={eof, ChannelId}} ->
 	    collect_data_report_end(Acc, Msg, EchoSize);
-
 	{ssh_cm, ConnectionRef, Msg={closed,ChannelId}} ->
 	    collect_data_report_end(Acc, Msg, EchoSize);
-
 	Msg ->
 	    ct:log("~p:~p collect_data: ***** unexpected message *****~n~p",[?MODULE,?LINE,Msg]),
 	    collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum)
-
     after TO ->
 	    ct:log("~p:~p collect_data: ----- Nothing received for ~p seconds -----~n",[?MODULE,?LINE,TO]),
 	    collect_data(ConnectionRef, ChannelId, EchoSize, Acc, Sum)
@@ -1686,6 +2187,11 @@ start_our_shell(_User, _Peer) ->
 		  %% Don't actually loop, just exit
           end).
 
+start_our_shell2(_User, _Peer) ->
+    spawn(fun() ->
+                  io:format("Enter command\n"),
+                  read_write_loop1("> ", 1)
+          end).
 
 ssh_exec_echo(Cmd) ->
     spawn(fun() ->
@@ -1696,3 +2202,24 @@ ssh_exec_echo(Cmd, User) ->
     spawn(fun() ->
                   io:format("echo ~s ~s\n",[User,Cmd])
           end).
+%% FIXME - upon refactoring this test suite, check if function below is reduntant to collect_data
+receive_bytes(_, _, 0, _) ->
+    ct:log("ALL DATA RECEIVED Budget = 0"),
+    ct:log("================================ ExpectBudget = 0 (reception completed)"),
+    ok;
+receive_bytes(ConnectionRef, ChannelId0, Budget, AccSize) when Budget > 0 ->
+    receive
+        {ssh_cm, ConnectionRef, {data, ChannelId0, 0, D}} ->
+            Fmt = "================================ ExpectBudget = "
+                "~p bytes Received/Total = ~p/~p bytes",
+            Args = [Budget, byte_size(D), AccSize + byte_size(D)],
+            ct:log(Fmt, Args),
+            ssh_connection:adjust_window(ConnectionRef, ChannelId0, size(D)),
+            receive_bytes(ConnectionRef, ChannelId0,
+                          Budget - byte_size(D), AccSize + byte_size(D))
+    after
+        10000 ->
+            ct:log("process_info(self(), messages) = ~p",
+                   [process_info(self(), messages)]),
+            ct:fail("timeout ~p:~p",[?MODULE,?LINE])
+    end.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh.cover
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh.cover
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh.cover
@@ -6,8 +6,8 @@
   ssh_app,
 
   %% %% Supervisors
-  %% ssh_acceptor_sup, ssh_channel_sup, ssh_connection_sup,
-  %% sshc_sup, sshd_sup, ssh_subsystem_sup, ssh_sup,
+  %% ssh_acceptor_sup, ssh_channel_sup,
+  %% sshc_sup, sshd_sup, ssh_connection_sup, ssh_sup,
   %% ssh_system_sup, ssh_tcpip_forward_acceptor_sup,
 
   %% Test and/or info modules:
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_dbg_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_dbg_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_dbg_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -336,6 +336,12 @@ dbg_ssh_messages(Config) ->
     ?DBG_RECEIVE("Received SSH_MSG_KEXINIT:",      Ref, C, Pid),
 
     case atom_to_list( (ssh_connection_handler:alg(C))#alg.kex ) of
+        "curve"++_ ->
+            ?DBG_RECEIVE("Going to send SSH_MSG_KEX_ECDH_INIT:",  Ref, C, Pid),
+            ?DBG_RECEIVE("Received SSH_MSG_KEX_ECDH_INIT:",       Ref, D, Pid),
+            ?DBG_RECEIVE("Going to send SSH_MSG_KEX_ECDH_REPLY:", Ref, D, Pid),
+            ?DBG_RECEIVE("Received SSH_MSG_KEX_ECDH_REPLY:",      Ref, C, Pid);
+
         "ecdh-"++_ ->
             ?DBG_RECEIVE("Going to send SSH_MSG_KEX_ECDH_INIT:",  Ref, C, Pid),
             ?DBG_RECEIVE("Received SSH_MSG_KEX_ECDH_INIT:",       Ref, D, Pid),
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_echo_server.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_echo_server.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_echo_server.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -27,7 +27,8 @@
 	  n,
 	  id,
 	  cm,
-	  dbg = false
+	  dbg = false,
+          parent
 	 }).
 -export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
 
@@ -42,13 +43,19 @@ init([N]) ->
     {ok, #state{n = N}};
 init([N,Opts]) ->
     State = #state{n = N,
-		   dbg = proplists:get_value(dbg,Opts,false)
+		   dbg = proplists:get_value(dbg,Opts,false),
+                   parent = proplists:get_value(parent, Opts)
 		  },
     ?DBG(State, "init([~p])",[N]),
     {ok, State}.
 
 handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
     ?DBG(State, "ssh_channel_up Cid=~p ConnMngr=~p",[ChannelId,ConnectionManager]),
+    Pid = State#state.parent,
+    if Pid /= undefined ->
+            Pid ! {conn_peer, ConnectionManager};
+       true -> ok
+    end,
     {ok, State#state{id = ChannelId,
 		     cm = ConnectionManager}}.
 
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_limited.cover
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_limited.cover
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_limited.cover
@@ -8,8 +8,8 @@
   ssh_app,
 
   %% Supervisors
-  ssh_acceptor_sup, ssh_channel_sup, ssh_connection_sup,
-  sshc_sup, sshd_sup, ssh_subsystem_sup, ssh_sup,
+  ssh_acceptor_sup, ssh_channel_sup,
+  sshc_sup, sshd_sup, ssh_connection_sup, ssh_sup,
   ssh_system_sup, ssh_tcpip_forward_acceptor_sup,
 
   %% Test and/or info modules:
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_options_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_options_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_options_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
          auth_method_kb_interactive_data_tuple/1,
          auth_method_kb_interactive_data_fun3/1,
          auth_method_kb_interactive_data_fun4/1,
+         auth_none/1,
          connectfun_disconnectfun_client/1, 
 	 disconnectfun_option_client/1, 
 	 disconnectfun_option_server/1, 
@@ -83,7 +84,12 @@
          save_accepted_host_option/1,
          raw_option/1,
          config_file/1,
-         config_file_modify_algorithms_order/1
+         config_file_modify_algorithms_order/1,
+         daemon_replace_options_simple/1,
+         daemon_replace_options_algs/1,
+         daemon_replace_options_algs_connect/1,
+         daemon_replace_options_algs_conf_file/1,
+         daemon_replace_options_not_found/1
 	]).
 
 %%% Common test callbacks
@@ -93,6 +99,10 @@
 	 init_per_testcase/2, end_per_testcase/2
 	]).
 
+%%% For test nodes
+-export([get_preferred_algorithms/2
+        ]).
+
 -define(NEWLINE, <<"\r\n">>).
 
 %%--------------------------------------------------------------------
@@ -115,6 +125,7 @@ all() ->
      auth_method_kb_interactive_data_tuple,
      auth_method_kb_interactive_data_fun3,
      auth_method_kb_interactive_data_fun4,
+     auth_none,
      {group, dir_options},
      ssh_connect_timeout,
      ssh_connect_arg4_timeout,
@@ -145,6 +156,11 @@ all() ->
      raw_option,
      config_file,
      config_file_modify_algorithms_order,
+     daemon_replace_options_simple,
+     daemon_replace_options_algs,
+     daemon_replace_options_algs_connect,
+     daemon_replace_options_algs_conf_file,
+     daemon_replace_options_not_found,
      {group, hardening_tests}
     ].
 
@@ -260,6 +276,7 @@ server_password_option(Config) when is_l
     
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "vego"},
 				 {password, "foo"},
 				 {user_interaction, false},
@@ -292,12 +309,14 @@ server_userpassword_option(Config) when
 
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "foo"},
 				 {password, "morot"},
 				 {user_interaction, false},
 				 {user_dir, UserDir}]),
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "vego"},
 				 {password, "foo"},
 				 {user_interaction, false},
@@ -327,12 +346,14 @@ server_pwdfun_option(Config) ->
     
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "foo"},
 				 {password, "morot"},
 				 {user_interaction, false},
 				 {user_dir, UserDir}]),
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "vego"},
 				 {password, "foo"},
 				 {user_interaction, false},
@@ -373,12 +394,14 @@ server_pwdfun_4_option(Config) ->
     
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "foo"},
 				 {password, "morot"},
 				 {user_interaction, false},
 				 {user_dir, UserDir}]),
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "fie"},
 				 {password, "morot"},
 				 {user_interaction, false},
@@ -392,6 +415,7 @@ server_pwdfun_4_option(Config) ->
 
     {error, Reason} =
 	ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
 				 {user, "bandit"},
 				 {password, "pwd breaking"},
 				 {user_interaction, false},
@@ -568,6 +592,30 @@ amkid(Config, {ExpectName,ExpectInstr,Ex
 			{"bar",2}]).
 
 %%--------------------------------------------------------------------
+auth_none(Config) ->
+    PrivDir = proplists:get_value(priv_dir, Config),
+    UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+    file:make_dir(UserDir),
+    SysDir = proplists:get_value(data_dir, Config),
+    {DaemonRef, Host, Port} =
+	ssh_test_lib:daemon([{system_dir, SysDir},
+			     {user_dir, UserDir},
+			     {auth_methods, "password"}, % to make even more sure we don't use public-key-auth
+			     {user_passwords, [{"foo","somepwd"}]}, % Not to be used
+                             {no_auth_needed, true} % we test this
+			    ]),
+    ClientConnRef1 =
+	ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+					  {user, "some-other-user"},
+					  {password, "wrong-pwd"},
+					  {user_dir, UserDir},
+					  {user_interaction, false}]),
+    "some-other-user" =
+        proplists:get_value(user, ssh:connection_info(ClientConnRef1, [user])),
+    ok = ssh:close(ClientConnRef1),
+    ok = ssh:stop_daemon(DaemonRef).
+
+%%--------------------------------------------------------------------
 system_dir_option(Config) ->
     DirUnread = proplists:get_value(unreadable_dir,Config),
     FileRead = proplists:get_value(readable_file,Config),
@@ -595,14 +643,16 @@ user_dir_option(Config) ->
     %% Any port will do (beware, implementation knowledge!):
     Port = 65535,
 
-    case ssh:connect("localhost", Port, [{user_dir, DirUnread}]) of
+    case ssh:connect("localhost", Port, [{user_dir, DirUnread},
+                                         {save_accepted_host, false}]) of
 	{error,{eoptions,{{user_dir,DirUnread},eacces}}} ->
 	    ok;
 	{error,econnrefused} ->
 	    ct:fail("Didn't detect that dir is unreadable", [])
     end,
 
-    case ssh:connect("localhost", Port, [{user_dir, FileRead}]) of
+    case ssh:connect("localhost", Port, [{user_dir, FileRead},
+                                         {save_accepted_host, false}]) of
 	{error,{eoptions,{{user_dir,FileRead},enotdir}}} ->
 	    ok;
 	{error,econnrefused} ->
@@ -972,13 +1022,13 @@ do_hostkey_fingerprint_check(Config, Has
 	true ->
 	    really_do_hostkey_fingerprint_check(Config, HashAlg);
 	false when HashAlg == old ->
-	    {skip,{unsupported_hash,md5}};% Happen to know that public_key:ssh_hostkey_fingerprint/1 uses md5...
+	    {skip,{unsupported_hash,md5}};% Happen to know that ssh:hostkey_fingerprint/1 uses md5...
 	false ->
 	    {skip,{unsupported_hash,HashAlg}}
     end.
 
 supported_hash(old) ->
-    supported_hash(md5); % Happen to know that public_key:ssh_hostkey_fingerprint/1 uses md5...
+    supported_hash(md5); % Happen to know that ssh:hostkey_fingerprint/1 uses md5...
 supported_hash(HashAlg) ->
     Hs = if is_atom(HashAlg) -> [HashAlg];
             is_list(HashAlg) -> HashAlg
@@ -990,11 +1040,11 @@ really_do_hostkey_fingerprint_check(Conf
     UserDir = proplists:get_value(user_dir, Config),
     SysDir = proplists:get_value(data_dir, Config),
 
-    %% All host key fingerprints.  Trust that public_key has checked the ssh_hostkey_fingerprint
+    %% All host key fingerprints.  Trust that public_key has checked the hostkey_fingerprint
     %% function since that function is used by the ssh client...
     FPs0 = [case HashAlg of
-	       old -> public_key:ssh_hostkey_fingerprint(Key);
-	       _ -> public_key:ssh_hostkey_fingerprint(HashAlg, Key)
+	       old -> ssh:hostkey_fingerprint(Key);
+	       _ -> ssh:hostkey_fingerprint(HashAlg, Key)
 	   end
 	   || FileCandidate <- begin
 				   {ok,KeyFileCands} = file:list_dir(SysDir),
@@ -1003,7 +1053,7 @@ really_do_hostkey_fingerprint_check(Conf
 	      nomatch =/= re:run(FileCandidate, ".*\\.pub", []),
 	      {Key,_Cmnts} <- begin
 				  {ok,Bin} = file:read_file(filename:join(SysDir, FileCandidate)),
-				  try public_key:ssh_decode(Bin, public_key)
+				  try ssh_file:decode(Bin, public_key)
 				  catch
 				      _:_ -> []
 				  end
@@ -1050,6 +1100,7 @@ ssh_connect_timeout(_Config) ->
     {error,{faked_transport,connect,TimeoutToTransport}} = 
 	ssh:connect("localhost", 12345, 
 		    [{transport,{tcp,?MODULE,tcp_closed}},
+                     {save_accepted_host, false},
 		     {connect_timeout,ConnTimeout}],
 		    1000),
     case TimeoutToTransport of
@@ -1088,7 +1139,7 @@ ssh_connect_arg4_timeout(_Config) ->
     %% try to connect with a timeout, but "supervise" it
     Client = spawn(fun() ->
 			   T0 = erlang:monotonic_time(),
-			   Rc = ssh:connect("localhost",Port,[],Timeout),
+			   Rc = ssh:connect("localhost",Port,[{save_accepted_host, false}],Timeout),
 			   ct:log("Client ssh:connect got ~p",[Rc]),
 			   Parent ! {done,self(),Rc,T0}
 		   end),
@@ -1156,7 +1207,7 @@ ssh_daemon_minimal_remote_max_packet_siz
 %% This test try every algorithm by connecting to an Erlang server
 id_string_no_opt_client(Config) ->
     {Server, _Host, Port} = fake_daemon(Config),
-    {error,_} = ssh:connect("localhost", Port, [], 1000),
+    {error,_} = ssh:connect("localhost", Port, [{save_accepted_host, false}], 1000),
     receive
 	{id,Server,"SSH-2.0-Erlang/"++Vsn} ->
 	    true = expected_ssh_vsn(Vsn);
@@ -1169,7 +1220,9 @@ id_string_no_opt_client(Config) ->
 %%--------------------------------------------------------------------
 id_string_own_string_client(Config) ->
     {Server, _Host, Port} = fake_daemon(Config),
-    {error,_} = ssh:connect("localhost", Port, [{id_string,"Pelle"}], 1000),
+    {error,_} = ssh:connect("localhost", Port, [{id_string,"Pelle"},
+                                                {save_accepted_host, false}
+                                               ], 1000),
     receive
 	{id,Server,"SSH-2.0-Pelle\r\n"} ->
 	    ok;
@@ -1182,7 +1235,8 @@ id_string_own_string_client(Config) ->
 %%--------------------------------------------------------------------
 id_string_own_string_client_trail_space(Config) ->
     {Server, _Host, Port} = fake_daemon(Config),
-    {error,_} = ssh:connect("localhost", Port, [{id_string,"Pelle "}], 1000),
+    {error,_} = ssh:connect("localhost", Port, [{id_string,"Pelle "},
+                                                {save_accepted_host, false}], 1000),
     receive
 	{id,Server,"SSH-2.0-Pelle \r\n"} ->
 	    ok;
@@ -1195,7 +1249,8 @@ id_string_own_string_client_trail_space(
 %%--------------------------------------------------------------------
 id_string_random_client(Config) ->
     {Server, _Host, Port} = fake_daemon(Config),
-    {error,_} = ssh:connect("localhost", Port, [{id_string,random}], 1000),
+    {error,_} = ssh:connect("localhost", Port, [{id_string,random},
+                                                {save_accepted_host, false}], 1000),
     receive
 	{id,Server,Id="SSH-2.0-Erlang"++_} ->
 	    ct:fail("Unexpected id: ~s.",[Id]);
@@ -1376,12 +1431,14 @@ max_log_item_len(Config) ->
     {ok, Reports} = ssh_eqc_event_handler:get_reports(ReportHandlerPid),
     ct:log("~p:~p ssh:connect -> ~p~n~p", [?MODULE,?LINE,R,Reports]),
 
-    [ok,ok] =
-        [check_skip_part(
-           string:tokens(
-             lists:flatten(io_lib:format(Fmt,Args)),
-             " \n"))
-         || {info_msg,_,{_,Fmt,Args}} <- Reports].
+    [ok] =
+        lists:usort(
+          [check_skip_part(
+             string:tokens(
+               lists:flatten(io_lib:format(Fmt,Args)),
+               " \n"))
+           || {info_msg,_,{_,Fmt,Args}} <- Reports]
+          ).
 
 
 check_skip_part(["Disconnect","...","("++_NumSkipped, "bytes","skipped)"]) ->
@@ -1426,6 +1483,7 @@ connect_fun(ssh_sftp__start_channel, _Co
 	    {ok,_Pid,ConnRef} =
 		ssh_sftp:start_channel(Host, Port, 
 				       [{silently_accept_hosts, true},
+                                        {save_accepted_host, false},
 					{user, "carni"},
 					{password, "meat"}
 				       ]),
@@ -1457,7 +1515,7 @@ max_sessions(Config, ParallelLogin, Conn
 	    ct:log("Connections up: ~p",[Connections]),
 	    [_|_] = Connections,
 
-	    %% N w try one more than alowed:
+	    %% N w try one more than allowed:
 	    ct:pal("Info Report expected here (if not disabled) ...",[]),
 	    try Connect(Host,Port)
 	    of
@@ -1487,7 +1545,7 @@ try_to_connect(Connect, Host, Port, Pid,
      of
 	 _ConnectionRef1 ->
 	     timer:cancel(Tref),
-             ct:log("Step 3 ok: could set up one more connection after killing one. Thats good.",[]),
+             ct:log("Step 3 ok: could set up one more connection after killing one. That's good.",[]),
 	     ssh:stop_daemon(Pid),
 	     receive % flush. 
 		 timeout_no_connection -> ok
@@ -1515,7 +1573,7 @@ max_sessions_drops_tcp_connects(Config)
     FloodSessions = 1000,
     ParallelLogin = true,
     NegTimeOut = 8*1000,
-    HelloTimeOut = 1*1000,
+    HelloTimeOut = 200,
 
     %% Start a test daemon
     SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
@@ -1538,6 +1596,7 @@ max_sessions_drops_tcp_connects(Config)
     SSHconnect = fun(N) ->
                          R = ssh:connect(Host, Port, 
                                          [{silently_accept_hosts, true},
+                                          {save_accepted_host, false},
                                           {user_dir, proplists:get_value(priv_dir,Config)},
                                           {user_interaction, false},
                                           {user, "carni"},
@@ -1600,10 +1659,10 @@ save_accepted_host_option(Config) ->
     {error,enoent} = file:read_file(KnownHosts),
 
     {ok,_C1} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                        {save_accepted_host, false},
                                         {user, "vego"},
                                         {password, "morot"},
                                         {user_interaction, false},
-                                        {save_accepted_host, false},
                                         {user_dir, UserDir}]),
     {error,enoent} = file:read_file(KnownHosts),
     
@@ -1630,6 +1689,18 @@ config_file(Config) ->
     ct:log("ServerAlgs =~n~p~n~nOurAlgs =~n~p~n~nCommonAlgs =~n~p",[ServerAlgs,OurAlgs,CommonAlgs]),   
     Nkex = length(proplists:get_value(kex, CommonAlgs, [])),
 
+    %% Adjust for very old ssh daemons that only supports ssh-rsa and ssh-dss:
+    AdjustClient =
+        case proplists:get_value(public_key,ServerAlgs,[]) -- ['ssh-rsa','ssh-dss'] of
+            [] ->
+                %% Old, let the client support them also:
+                ct:log("Adjust the client's public_key set", []),
+                [{public_key, ['ssh-rsa','ssh-dss']}];
+            [_|_] ->
+                %% Ok, let the client be un-modified:
+                []
+        end,
+
     case {ServerAlgs, ssh_test_lib:some_empty(CommonAlgs)} of
         {[],_} ->
             {skip, "No server algorithms found"};
@@ -1643,30 +1714,24 @@ config_file(Config) ->
             [{_,[Ch1|_]}|_] = proplists:get_value(cipher, CommonAlgs),
 
             %% Make config file:
-            Contents = 
-                [{ssh, [{preferred_algorithms,
-                         [{cipher, [Ch1]},
-                          {kex,    [K1a]}
-                         ]},
-                        {client_options,
-                         [{modify_algorithms,
-                           [{rm,     [{kex, [K1a]}]},
-                            {append, [{kex, [K1b]}]}
+            {ok,ConfFile} = 
+                make_config_file_in_privdir(
+                  "c2.config", Config,
+                  [{ssh, [{preferred_algorithms,
+                           [{cipher, [Ch1]},
+                            {kex,    [K1a]}
+                           ] ++ AdjustClient},
+                          {client_options,
+                           [{modify_algorithms,
+                             [{rm,     [{kex, [K1a]}]},
+                              {append, [{kex, [K1b]}]}
+                             ]}
                            ]}
                          ]}
-                       ]}
-                ],          
-            %% write the file:
-            PrivDir = proplists:get_value(priv_dir, Config),
-            ConfFile = filename:join(PrivDir,"c2.config"),
-            {ok,D} = file:open(ConfFile, [write]),
-            io:format(D, "~p.~n", [Contents]),
-            file:close(D),
-            {ok,Cnfs} = file:read_file(ConfFile),
-            ct:log("c2.config:~n~s", [Cnfs]),
+                  ]),
 
             %% Start the slave node with the configuration just made:
-            {ok,Node} = start_node(random_node_name(?MODULE), ConfFile),
+            {ok, Peer, Node} = ?CT_PEER(["-config", ConfFile]),
 
             R0 = rpc:call(Node, ssh, default_algorithms, []),
             ct:log("R0 = ~p",[R0]),
@@ -1683,9 +1748,11 @@ config_file(Config) ->
                       {_,[Ch1]}]} | _] = R1,
 
             %% First connection. The client_options should be applied:
-            {ok,C1} = rpc:call(Node, ssh, connect, [loopback, 22, [{silently_accept_hosts, true},
-                                                                   {user_interaction, false}
-                                                                  ]]),
+            {ok,C1} = rpc:call(Node, ssh, connect, [loopback, ?SSH_DEFAULT_PORT,
+                                                    [{silently_accept_hosts, true},
+                                                     {save_accepted_host, false},
+                                                     {user_interaction, false}
+                                                    ]]),
             ct:log("C1 = ~n~p", [C1]),
             {algorithms,As1} = rpc:call(Node, ssh, connection_info, [C1, algorithms]),
             K1b = proplists:get_value(kex, As1),
@@ -1698,9 +1765,10 @@ config_file(Config) ->
             C2_Opts = [{modify_algorithms,[{rm,[{kex,[K1b]}]}, % N.B.
                                            {append, [{kex,[K2a]}]}]},
                        {silently_accept_hosts, true},
+                       {save_accepted_host, false},
                        {user_interaction, false}
                       ],
-            {ok,C2} = rpc:call(Node, ssh, connect, [loopback, 22, C2_Opts]),
+            {ok,C2} = rpc:call(Node, ssh, connect, [loopback, ?SSH_DEFAULT_PORT, C2_Opts]),
             {algorithms,As2} = rpc:call(Node, ssh, connection_info, [C2, algorithms]),
             K2a = proplists:get_value(kex, As2),
             Ch1 = proplists:get_value(encrypt, As2),
@@ -1708,7 +1776,7 @@ config_file(Config) ->
             {options,Os2} = rpc:call(Node, ssh, connection_info, [C2, options]),
             ct:log("C2 opts:~n~p~n~nalgorithms:~n~p~n~noptions:~n~p", [C2_Opts,As2,Os2]),
 
-            stop_node_nice(Node)
+            peer:stop(Peer)
     end.
     
 %%%----------------------------------------------------------------
@@ -1732,36 +1800,30 @@ config_file_modify_algorithms_order(Conf
             [{_,[Ch1|_]}|_] = proplists:get_value(cipher, CommonAlgs),
 
             %% Make config file:
-            Contents = 
-                [{ssh, [{preferred_algorithms,
-                         [{cipher, [Ch1]},
-                          {kex,    [K1]}
-                         ]},
-                        {server_options,
-                         [{modify_algorithms,
-                           [{rm,     [{kex, [K1]}]},
-                            {append, [{kex, [K2]}]}
-                           ]}
-                         ]},
-                        {client_options,
-                         [{modify_algorithms,
-                           [{rm,     [{kex, [K1]}]},
-                            {append, [{kex, [K3]}]}
+            {ok, ConfFile} =
+                make_config_file_in_privdir(
+                  "c3.config", Config,
+                  [{ssh, [{preferred_algorithms,
+                           [{cipher, [Ch1]},
+                            {kex,    [K1]}
+                           ]},
+                          {server_options,
+                           [{modify_algorithms,
+                             [{rm,     [{kex, [K1]}]},
+                              {append, [{kex, [K2]}]}
+                             ]}
+                           ]},
+                          {client_options,
+                           [{modify_algorithms,
+                             [{rm,     [{kex, [K1]}]},
+                              {append, [{kex, [K3]}]}
+                             ]}
                            ]}
                          ]}
-                       ]}
-                ],          
-            %% write the file:
-            PrivDir = proplists:get_value(priv_dir, Config),
-            ConfFile = filename:join(PrivDir,"c3.config"),
-            {ok,D} = file:open(ConfFile, [write]),
-            io:format(D, "~p.~n", [Contents]),
-            file:close(D),
-            {ok,Cnfs} = file:read_file(ConfFile),
-            ct:log("c3.config:~n~s", [Cnfs]),
+                  ]),
 
             %% Start the slave node with the configuration just made:
-            {ok,Node} = start_node(random_node_name(?MODULE), ConfFile),
+            {ok, Peer, Node} = ?CT_PEER(["-config", ConfFile]),
     
             R0 = rpc:call(Node, ssh, default_algorithms, []),
             ct:log("R0 = ~p",[R0]),
@@ -1798,32 +1860,192 @@ config_file_modify_algorithms_order(Conf
             ConnOptions = proplists:get_value(options, ConnInfo),
             ConnPrefAlgs = proplists:get_value(preferred_algorithms, ConnOptions),
 
-            %% And now, are all levels appied in right order:
+            %% And now, are all levels applied in right order:
             [K3,K2] = proplists:get_value(kex, ConnPrefAlgs),
 
-            stop_node_nice(Node)
+            peer:stop(Peer)
     end.
 
     
 %%--------------------------------------------------------------------
-%% Internal functions ------------------------------------------------
+daemon_replace_options_simple(Config) ->
+    SysDir = proplists:get_value(data_dir, Config),
+
+    UserDir1 = proplists:get_value(user_dir, Config),
+    UserDir2 = filename:join(UserDir1, "foo"),
+    file:make_dir(UserDir2),
+
+    {Pid, _Host, _Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+                                               {user_dir, UserDir1}
+                                              ]),
+    {ok,Opts1} = ssh:daemon_info(Pid),
+    UserDir1 = proplists:get_value(user_dir, proplists:get_value(options,Opts1,[])),
+
+    {ok, Pid} = ssh:daemon_replace_options(Pid, [{user_dir,UserDir2}]),
+    {ok,Opts2} = ssh:daemon_info(Pid),
+    case proplists:get_value(user_dir, proplists:get_value(options,Opts2,[])) of
+        UserDir2 ->
+            ok;
+        UserDir1 ->
+            ct:log("~p:~p Got old value ~p~nExpected ~p", [?MODULE,?LINE,UserDir1,UserDir2]),
+            {fail, "Not changed"};
+        Other ->
+            ct:log("~p:~p Got ~p~nExpected ~p", [?MODULE,?LINE,Other,UserDir2]),
+            {fail, "Strange value"}
+    end.
+
 %%--------------------------------------------------------------------
+daemon_replace_options_algs(Config) ->
+    SysDir = proplists:get_value(data_dir, Config),
+    UserDir = proplists:get_value(user_dir, Config),
 
-start_node(Name, ConfigFile) ->
-    Pa = filename:dirname(code:which(?MODULE)),
-    test_server:start_node(Name, slave, [{args, 
-                                          " -pa " ++ Pa ++ 
-                                          " -config " ++ ConfigFile}]).
-
-stop_node_nice(Node) when is_atom(Node) ->
-    test_server:stop_node(Node).
-
-random_node_name(BaseName) ->
-    L = integer_to_list(erlang:unique_integer([positive])),
-    lists:concat([BaseName,"___",L]).
+    DefaultKex =
+        ssh_transport:default_algorithms(kex),
+    NonDefaultKex =
+        ssh_transport:supported_algorithms(kex) -- DefaultKex,
+
+    case NonDefaultKex of
+        [A1|_] ->
+            [A2,A3|_] = DefaultKex,
+            {Pid, _Host, _Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+                                                       {user_dir, UserDir},
+                                                       {preferred_algorithms,[{kex,[A1]}]}
+                                                      ]),
+            [A1] = get_preferred_algorithms(Pid, kex),
+            {ok, Pid} =
+                ssh:daemon_replace_options(Pid, [{modify_algorithms,
+                                                  [{prepend,[{kex,[A2]}]}]
+                                                 }
+                                                ]),
+            [A2,A1] = get_preferred_algorithms(Pid, kex),
+
+            {ok, Pid} =
+                ssh:daemon_replace_options(Pid, [{preferred_algorithms,[{kex,[A3]}]
+                                                 }
+                                                ]),
+            [A2,A3] = get_preferred_algorithms(Pid, kex)
+            ;
+        [] ->
+            {skip, "No non-default kex"}
+    end.
+
+%%--------------------------------------------------------------------
+daemon_replace_options_algs_connect(Config) ->
+    [A1,A2|_] =
+        ssh_transport:default_algorithms(kex),
+
+    {Pid, Host, Port} =
+        ssh_test_lib:std_daemon(Config,
+                                [{preferred_algorithms,[{kex,[A1]}]}
+                                ]),
+    [A1] = get_preferred_algorithms(Pid, kex),
+
+    %% Open a connection with A1 as kex and test it
+    C1 =
+        ssh_test_lib:std_connect(Config, Host, Port,
+                                 [{preferred_algorithms,[{kex,[A1]}]}
+                                 ]),
+    ok = test_connection(C1),
+    ok = test_not_connect(Config, Host, Port,
+                          [{preferred_algorithms,[{kex,[A2]}]}
+                          ]),
+
+    %% Change kex to A2
+    {ok, Pid} =
+        ssh:daemon_replace_options(Pid,
+                                   [{preferred_algorithms,[{kex,[A2]}]}]),
+    [A2] = get_preferred_algorithms(Pid, kex),
+
+    %% and open the second connection with this kex, and test it
+    C2 =
+        ssh_test_lib:std_connect(Config, Host, Port,
+                                 [{preferred_algorithms,[{kex,[A2]}]}
+                                 ]),
+    ok = test_connection(C2),
+    ok = test_not_connect(Config, Host, Port,
+                          [{preferred_algorithms,[{kex,[A1]}]}
+                          ]),
+
+    %% Test that the first connection is still alive:
+    ok = test_connection(C1),
+
+    ssh:close(C1),
+    ssh:close(C2),
+    ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+daemon_replace_options_algs_conf_file(Config) ->
+    SysDir = proplists:get_value(data_dir, Config),
+    UserDir = proplists:get_value(user_dir, Config),
+
+    DefaultKex =
+        ssh_transport:default_algorithms(kex),
+    NonDefaultKex =
+        ssh_transport:supported_algorithms(kex) -- DefaultKex,
+
+    case NonDefaultKex of
+        [A0,A1|_] ->
+            %% Make config file:
+            {ok,ConfFile} =
+                make_config_file_in_privdir(
+                  "c4.config", Config,
+                  [{ssh, [{modify_algorithms,
+                           %% Whatever happens, always put A0 first in the kex list:
+                           [{prepend, [{kex, [A0]}]}
+                           ]}
+                         ]}
+                  ]),
+
+            [A2|_] = DefaultKex,
+            ct:log("[A0, A1, A2] = ~p", [[A0, A1, A2]]),
+
+            %% Start the slave node with the configuration just made:
+            {ok, Peer, Node} = ?CT_PEER(["-config", ConfFile]),
+
+            %% Start ssh on the slave. This should apply the ConfFile:
+            rpc:call(Node, ssh, start, []),
+
+            {Pid, _Host, _Port} =
+                rpc:call(Node, ssh_test_lib, daemon,
+                         [
+                          [{system_dir, SysDir},
+                           {user_dir, UserDir},
+                           {preferred_algorithms,[{kex,[A1]}]}
+                          ]
+                         ]),
+
+            [A0,A1] =
+                rpc:call(Node, ?MODULE, get_preferred_algorithms, [Pid, kex]),
+            {ok, Pid} =
+                rpc:call(Node, ssh, daemon_replace_options,
+                         [Pid,
+                          [{modify_algorithms,
+                            [{prepend,[{kex,[A2]}]}]
+                           }
+                          ]
+                         ]),
+
+            %% Check that the precedens order is fulfilled:
+            [A2,A0,A1] =
+                rpc:call(Node, ?MODULE, get_preferred_algorithms, [Pid, kex]),
+
+            peer:stop(Peer);
+        [] ->
+            {skip, "No non-default kex"}
+    end.
+
+%%--------------------------------------------------------------------
+daemon_replace_options_not_found(_Config) ->
+    %% when the daemon doesn't exist the error should be the same
+    %% in daemon_info and daemon_replace_options
+    %% which is {error, bad_daemon_ref}
+    Error = ssh:daemon_info(self()),
+    Error = ssh:daemon_replace_options(self(), []).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
 
-%%%----
-  
 expected_ssh_vsn(Str) ->
     try
 	{ok,L} = application:get_all_key(ssh),
@@ -1833,7 +2055,7 @@ expected_ssh_vsn(Str) ->
 	"\r\n" -> true;
 	_ -> false
     catch
-	_:_ -> true %% ssh not started so we dont't know
+	_:_ -> true %% ssh not started so we don't know
     end.
 	    
 
@@ -1860,3 +2082,42 @@ fake_daemon(_Config) ->
     after 
 	10000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
     end.
+
+
+make_config_file_in_privdir(FileName, Config, Contents) ->
+    %% write the file:
+    PrivDir = proplists:get_value(priv_dir, Config),
+    ConfFile = filename:join(PrivDir, FileName),
+    {ok,D} = file:open(ConfFile, [write]),
+    io:format(D, "~p.~n", [Contents]),
+    file:close(D),
+    {ok,Cnfs} = file:read_file(ConfFile),
+    ct:log("Config file ~p :~n~s", [ConfFile,Cnfs]),
+    {ok,ConfFile}.
+
+
+get_preferred_algorithms(Pid, Type) ->
+    {ok,#{preferred_algorithms:=As}} = ssh_system_sup:get_acceptor_options(Pid),
+    proplists:get_value(Type, As).
+
+test_connection(C) ->
+    {ok, Ch} = ssh_connection:session_channel(C, infinity),
+    A = rand:uniform(100),
+    B = rand:uniform(100),
+    A_plus_B = lists:concat([A,"+",B,"."]),
+    Sum = integer_to_binary(A+B),
+    success = ssh_connection:exec(C, Ch, A_plus_B, infinity),
+    expected = ssh_test_lib:receive_exec_result(
+                 {ssh_cm, C, {data, Ch, 0, Sum}} ),
+    ssh_test_lib:receive_exec_end(C, Ch),
+    ok.
+
+test_not_connect(Config, Host, Port, Opts) ->
+    try
+        ssh_test_lib:std_connect(Config, Host, Port, Opts)
+    of
+        Cx when is_pid(Cx) -> {error, connected}
+    catch
+        error:{badmatch, {error,_}} -> ok
+    end.
+
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_protocol_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_protocol_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -1,18 +1,19 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
 %%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
 %%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
 %%
 %% %CopyrightEnd%
 %%
@@ -25,6 +26,7 @@
 -include_lib("kernel/include/inet.hrl").
 -include("ssh.hrl").		% ?UINT32, ?BYTE, #ssh{} ...
 -include("ssh_transport.hrl").
+-include("ssh_connect.hrl").
 -include("ssh_auth.hrl").
 -include("ssh_test_lib.hrl").
 
@@ -47,12 +49,19 @@
          bad_service_name_then_correct/1,
          bad_very_long_service_name/1,
          client_handles_keyboard_interactive_0_pwds/1,
+         client_handles_banner_keyboard_interactive/1,
          client_info_line/1,
          do_gex_client_init/3,
          do_gex_client_init_old/3,
          empty_service_name/1,
          ext_info_c/1,
          ext_info_s/1,
+         kex_strict_negotiated/1,
+         kex_strict_violation_key_exchange/1,
+         kex_strict_violation_new_keys/1,
+         kex_strict_violation/1,
+         kex_strict_violation_2/1,
+         kex_strict_msg_unknown/1,
          gex_client_init_option_groups/1,
          gex_client_init_option_groups_file/1,
          gex_client_init_option_groups_moduli_file/1,
@@ -69,6 +78,8 @@
          modify_rm/1,
          no_common_alg_client_disconnects/1,
          no_common_alg_server_disconnects/1,
+         custom_kexinit/1,
+         early_rce/1,
          no_ext_info_s1/1,
          no_ext_info_s2/1,
          packet_length_too_large/1,
@@ -76,7 +87,9 @@
          preferred_algorithms/1,
          service_name_length_too_large/1,
          service_name_length_too_short/1,
-         client_close_after_hello/1
+         client_close_after_hello/1,
+         channel_close_timeout/1,
+         extra_ssh_msg_service_request/1
         ]).
 
 -define(NEWLINE, <<"\r\n">>).
@@ -90,11 +103,19 @@
                                    [{client2server,Ciphs}, {server2client,Ciphs}]
                           end)()
         ).
-
-
 -define(v(Key, Config), proplists:get_value(Key, Config)).
 -define(v(Key, Config, Default), proplists:get_value(Key, Config, Default)).
-
+-define(HARDCODED_KEXDH_REPLY,
+        #ssh_msg_kexdh_reply{
+           public_host_key = {{{'ECPoint',<<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
+                               {namedCurve,{1,3,101,112}}},
+                              'ssh-ed25519'},
+           f = 18504393053016436370762156176197081926381112956345797067569792020930728564439992620494295053804030674742529174859108487694089045521619258420515443400605141150065440678508889060925968846155921972385560196703381004650914261218463420313738628465563288022895912907728767735629532940627575655703806353550720122093175255090704443612257683903495753071530605378193139909567971489952258218767352348904221407081210633467414579377014704081235998044497191940270966762124544755076128392259615566530695493013708460088312025006678879288856957348606386230195080105197251789635675011844976120745546472873505352732719507783227210178188,
+           h_sig = <<90,247,44,240,136,196,82,215,56,165,53,33,230,101,253,
+                     34,112,201,21,131,162,169,10,129,174,14,69,25,39,174,
+                     92,210,130,249,103,2,215,245,7,213,110,235,136,134,11,
+                     124,248,139,79,17,225,77,125,182,204,84,137,167,99,186,
+                     167,42,192,10>>}).
 
 %%--------------------------------------------------------------------
 %% Common Test interface functions -----------------------------------
@@ -107,6 +128,7 @@ suite() ->
 all() -> 
     [{group,tool_tests},
      client_info_line,
+     early_rce,
      {group,kex},
      {group,service_requests},
      {group,authentication},
@@ -114,7 +136,8 @@ all() ->
      {group,field_size_error},
      {group,ext_info},
      {group,preferred_algorithms},
-     {group,client_close_early}
+     {group,client_close_early},
+     {group,channel_close}
     ].
 
 groups() ->
@@ -125,26 +148,32 @@ groups() ->
 		      ]},
      {packet_size_error, [], [packet_length_too_large,
 			      packet_length_too_short]},
-      
      {field_size_error, [], [service_name_length_too_large,
 			     service_name_length_too_short]},
-      
-     {kex, [], [no_common_alg_server_disconnects,
+     {kex, [], [custom_kexinit,
+                no_common_alg_server_disconnects,
 		no_common_alg_client_disconnects,
 		gex_client_init_option_groups,
 		gex_server_gex_limit,
 		gex_client_init_option_groups_moduli_file,
 		gex_client_init_option_groups_file,
 		gex_client_old_request_exact,
-		gex_client_old_request_noexact
-		]},
+		gex_client_old_request_noexact,
+                kex_strict_negotiated,
+                kex_strict_violation_key_exchange,
+                kex_strict_violation_new_keys,
+                kex_strict_violation,
+                kex_strict_violation_2,
+                kex_strict_msg_unknown]},
      {service_requests, [], [bad_service_name,
 			     bad_long_service_name,
 			     bad_very_long_service_name,
 			     empty_service_name,
-			     bad_service_name_then_correct
+			     bad_service_name_then_correct,
+                             extra_ssh_msg_service_request
 			    ]},
-     {authentication, [], [client_handles_keyboard_interactive_0_pwds
+     {authentication, [], [client_handles_keyboard_interactive_0_pwds,
+                           client_handles_banner_keyboard_interactive
 			  ]},
      {ext_info, [], [no_ext_info_s1,
                      no_ext_info_s2,
@@ -157,24 +186,24 @@ groups() ->
                                  modify_rm,
                                  modify_combo
                                 ]},
-     {client_close_early, [], [client_close_after_hello
-                               ]}
+     {client_close_early, [], [client_close_after_hello]},
+     {channel_close, [], [channel_close_timeout]}
     ].
 
 
 init_per_suite(Config) ->
     ?CHECK_CRYPTO(start_std_daemon( setup_dirs( start_apps(Config)))).
-    
+
 end_per_suite(Config) ->
     stop_apps(Config).
 
-
-
-init_per_testcase(no_common_alg_server_disconnects, Config) ->
+init_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects;
+                                   Tc == custom_kexinit ->
     start_std_daemon(Config, [{preferred_algorithms,[{public_key,['ssh-rsa']},
                                                      {cipher,?DEFAULT_CIPHERS}
                                                     ]}]);
-
+init_per_testcase(kex_strict_negotiated, Config) ->
+    Config;
 init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ;
 				   TC == gex_client_init_option_groups_moduli_file ;
 				   TC == gex_client_init_option_groups_file ;
@@ -215,8 +244,11 @@ init_per_testcase(TC, Config) when TC ==
 init_per_testcase(_TestCase, Config) ->
     check_std_daemon_works(Config, ?LINE).
 
-end_per_testcase(no_common_alg_server_disconnects, Config) ->
+end_per_testcase(Tc, Config) when Tc == no_common_alg_server_disconnects;
+                                  Tc == custom_kexinit ->
     stop_std_daemon(Config);
+end_per_testcase(kex_strict_negotiated, Config) ->
+    Config;
 end_per_testcase(TC, Config) when TC == gex_client_init_option_groups ;
 				  TC == gex_client_init_option_groups_moduli_file ;
 				  TC == gex_client_init_option_groups_file ;
@@ -374,6 +406,90 @@ no_common_alg_server_disconnects(Config)
 	  ]
 	 ).
 
+early_rce(Config) ->
+    {ok,InitialState} =
+        ssh_trpt_test_lib:exec([{set_options, [print_ops, print_seqnums, print_messages]}]),
+    TypeOpen = "session",
+    ChannelId = 0,
+    WinSz = 425984,
+    PktSz = 65536,
+    DataOpen = <<>>,
+    SshMsgChannelOpen = ssh_connection:channel_open_msg(TypeOpen, ChannelId, WinSz, PktSz, DataOpen),
+
+    Id = 0,
+    TypeReq = "exec",
+    WantReply = true,
+    DataReq = <<?STRING(<<"lists:seq(1,10).">>)>>,
+    SshMsgChannelRequest =
+        ssh_connection:channel_request_msg(Id, TypeReq, WantReply, DataReq),
+    {ok, _AfterKexState} =
+        ssh_trpt_test_lib:exec(
+          [{connect,
+            server_host(Config),server_port(Config),
+            [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                    {cipher,?DEFAULT_CIPHERS}
+                                   ]},
+             {silently_accept_hosts, true},
+             {recv_ext_info, false},
+             {user_dir, user_dir(Config)},
+             {user_interaction, false}
+            | proplists:get_value(extra_options,Config,[])]},
+           receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, SshMsgChannelOpen},
+           {send, SshMsgChannelRequest},
+           {match, disconnect(), receive_msg}
+          ], InitialState),
+    ok.
+
+custom_kexinit(Config) ->
+    %% 16#C0 value causes unicode:characters_to_list to return a big error value
+    Trash = lists:duplicate(260_000, 16#C0),
+    FunnyAlg = "curve25519-sha256",
+    KexInit =
+        #ssh_msg_kexinit{cookie = <<"Ã/Ï!9zñKá:ñÀv¿JÜ">>,
+                         kex_algorithms =
+                             [FunnyAlg ++ Trash],
+                         server_host_key_algorithms = ["ssh-rsa"],
+                         encryption_algorithms_client_to_server =
+                             ["aes256-ctr","aes192-ctr","aes128-ctr","aes128-cbc","3des-cbc"],
+                         encryption_algorithms_server_to_client =
+                             ["aes256-ctr","aes192-ctr","aes128-ctr","aes128-cbc","3des-cbc"],
+                         mac_algorithms_client_to_server =
+                             ["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com",
+                              "hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],
+                         mac_algorithms_server_to_client =
+                             ["hmac-sha2-512-etm@openssh.com","hmac-sha2-256-etm@openssh.com",
+                              "hmac-sha2-512","hmac-sha2-256","hmac-sha1-etm@openssh.com","hmac-sha1"],
+                         compression_algorithms_client_to_server = ["none","zlib@openssh.com","zlib"],
+                         compression_algorithms_server_to_client = ["none","zlib@openssh.com","zlib"],
+                         languages_client_to_server = [],
+                         languages_server_to_client = [],
+                         first_kex_packet_follows = false,
+                         reserved = 0
+                        },
+    {ok,_} =
+	ssh_trpt_test_lib:exec(
+	  [{set_options, [print_ops, {print_messages,detail}]},
+	   {connect,
+	    server_host(Config),server_port(Config),
+	    [{silently_accept_hosts, true},
+	     {user_dir, user_dir(Config)},
+	     {user_interaction, false},
+	     {preferred_algorithms,[{public_key,['ssh-rsa']},
+                                    {cipher,?DEFAULT_CIPHERS}
+                                   ]}
+	    ]},
+	   receive_hello,
+	   {send, hello},
+	   {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+	   {send, KexInit},  % with server unsupported 'ssh-dss' !
+	   {match, disconnect(), receive_msg}
+	  ]
+	 ).
+
 %%--------------------------------------------------------------------
 %%% Algo negotiation fail.  This should result in a ssh_msg_disconnect
 %%% being sent from the client.
@@ -682,7 +798,82 @@ client_handles_keyboard_interactive_0_pw
                                                 ]}]
 			).
 
+%%%--------------------------------------------------------------------
+%%% SSH_MSG_USERAUTH_BANNER can be sent at any time during user auth.
+%%% The following test mimics a SSH server implementation that sends the banner
+%%% immediately before sending SSH_MSG_USERAUTH_SUCCESS.
+client_handles_banner_keyboard_interactive(Config) ->
+    {User,_Pwd} = server_user_password(Config),
 
+    %% Create a listening socket as server socket:
+    {ok,InitialState} = ssh_trpt_test_lib:exec(listen),
+    HostPort = ssh_trpt_test_lib:server_host_port(InitialState),
+
+    %% Start a process handling one connection on the server side:
+    spawn_link(
+      fun() ->
+	      {ok,_} =
+		  ssh_trpt_test_lib:exec(
+		    [{set_options, [print_ops, print_messages]},
+		     {accept, [{system_dir, system_dir(Config)},
+			       {user_dir, user_dir(Config)}]},
+		     receive_hello,
+		     {send, hello},
+
+		     {send, ssh_msg_kexinit},
+		     {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+
+		     {match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
+		     {send, ssh_msg_kexdh_reply},
+
+		     {send, #ssh_msg_newkeys{}},
+		     {match,  #ssh_msg_newkeys{_='_'}, receive_msg},
+
+		     {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg},
+		     {send, #ssh_msg_service_accept{name="ssh-userauth"}},
+
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="none",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_failure{authentications = "keyboard-interactive",
+						      partial_success = false}},
+
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="keyboard-interactive",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_info_request{name = "",
+							   instruction = "",
+							   language_tag = "",
+							   num_prompts = 1,
+							   data = <<0,0,0,10,80,97,115,115,119,111,114,100,58,32,0>>
+							  }},
+		     {match, #ssh_msg_userauth_info_response{num_responses = 1,
+							     _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_info_request{name = "",
+							   instruction = "",
+							   language_tag = "",
+							   num_prompts = 0,
+							   data = <<>>
+							  }},
+		     {match, #ssh_msg_userauth_info_response{num_responses = 0,
+							     data = <<>>,
+							     _='_'}, receive_msg},
+                     {send, #ssh_msg_userauth_banner{message = "Banner\n"}},
+		     {send, #ssh_msg_userauth_success{}},
+		     close_socket,
+		     print_state
+		    ],
+		    InitialState)
+      end),
+
+    %% and finally connect to it with a regular Erlang SSH client:
+    {ok,_} = std_connect(HostPort, Config,
+			 [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                                 {cipher,?DEFAULT_CIPHERS}
+                                                ]}]
+			).
 
 %%%--------------------------------------------------------------------
 client_info_line(Config) ->
@@ -717,7 +908,7 @@ client_info_line(Config) ->
 %%% The server does not send the extension because
 %%% the client does not tell the server to send it
 no_ext_info_s1(Config) ->
-    %% Start the dameon
+    %% Start the daemon
     Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,true},
                                               {system_dir, system_dir(Config)}]),
     {ok,AfterKexState} = connect_and_kex([{server,Server}|Config]),
@@ -732,7 +923,7 @@ no_ext_info_s1(Config) ->
 %%% The server does not send the extension because
 %%% the server is not configured to send it
 no_ext_info_s2(Config) ->    
-    %% Start the dameon
+    %% Start the daemon
     Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,false},
                                               {system_dir, system_dir(Config)}]),
     {ok,AfterKexState} = connect_and_kex([{extra_options,[{recv_ext_info,true}]},
@@ -748,7 +939,7 @@ no_ext_info_s2(Config) ->
 %%%--------------------------------------------------------------------
 %%% The server sends the extension
 ext_info_s(Config) ->    
-    %% Start the dameon
+    %% Start the daemon
     Server = {Pid,_,_} = ssh_test_lib:daemon([{send_ext_info,true},
                                               {system_dir, system_dir(Config)}]),
     {ok,AfterKexState} = connect_and_kex([{extra_options,[{recv_ext_info,true}]},
@@ -818,6 +1009,226 @@ ext_info_c(Config) ->
         {result, Pid, Error} -> ct:fail("Error: ~p",[Error])
     end.
 
+%%%--------------------------------------------------------------------
+%%%
+kex_strict_negotiated(Config0) ->
+    {ok, TestRef} = ssh_test_lib:add_log_handler(),
+    Config = start_std_daemon(Config0, []),
+    {Server, Host, Port} = proplists:get_value(server, Config),
+    Level = ssh_test_lib:get_log_level(),
+    ssh_test_lib:set_log_level(debug),
+    {ok, ConnRef} = std_connect({Host, Port}, Config, []),
+    {algorithms, _A} = ssh:connection_info(ConnRef, algorithms),
+    ssh:stop_daemon(Server),
+    {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+    true = ssh_test_lib:kex_strict_negotiated(client, Events),
+    true = ssh_test_lib:kex_strict_negotiated(server, Events),
+    ssh_test_lib:set_log_level(Level),
+    ssh_test_lib:rm_log_handler(),
+    ok.
+
+%% Connect to an erlang server and inject unexpected SSH message
+%% ssh_fsm_kexinit in key_exchange state
+kex_strict_violation_key_exchange(Config) ->
+    ExpectedReason = "KEX strict violation",
+    Injections = [ssh_msg_ignore, ssh_msg_debug, ssh_msg_unimplemented],
+    TestProcedure =
+        fun(M) ->
+                ct:log(
+                  "=================== START: ~p Message: ~p Expected Fail =================================",
+                  [?FUNCTION_NAME, M]),
+                [receive_hello,
+                 {send, hello},
+                 {send, ssh_msg_kexinit},
+                 {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+                 {send, M},
+                 {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]
+        end,
+    [kex_strict_helper(Config, TestProcedure(Msg), ExpectedReason) ||
+        Msg <- Injections],
+    ct:log("========== END ========"),
+    ok.
+
+%% Connect to an erlang server and inject unexpected SSH message
+%% ssh_fsm_kexinit in new_keys state
+kex_strict_violation_new_keys(Config) ->
+    ExpectedReason = "KEX strict violation",
+    Injections = [ssh_msg_ignore, ssh_msg_debug, ssh_msg_unimplemented],
+    TestProcedure =
+        fun(M) ->
+                ct:log(
+                  "=================== START: ~p Message: ~p Expected Fail =================================",
+                  [?FUNCTION_NAME, M]),
+                [receive_hello,
+                 {send, hello},
+                 {send, ssh_msg_kexinit},
+                 {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+                 {send, ssh_msg_kexdh_init},
+                 {send, M},
+                 {match, #ssh_msg_kexdh_reply{_='_'}, receive_msg},
+                 {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]
+        end,
+    [kex_strict_helper(Config, TestProcedure(Msg), ExpectedReason) ||
+        Msg <- Injections],
+    ct:log("========== END ========"),
+    ok.
+
+%% Connect to an erlang server and inject unexpected SSH message
+%% duplicated KEXINIT
+kex_strict_violation(Config) ->
+    TestFlows =
+        [{kexinit, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, ssh_msg_kexinit},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]},
+         {ssh_msg_kexdh_init, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, ssh_msg_kexdh_init_dup},
+           {match,# ssh_msg_kexdh_reply{_='_'}, receive_msg},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]},
+         {new_keys, "Message ssh_msg_newkeys in wrong state",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           {send, ssh_msg_kexdh_init},
+           {match,# ssh_msg_kexdh_reply{_='_'}, receive_msg},
+           {send, #ssh_msg_newkeys{}},
+           {match, #ssh_msg_newkeys{_='_'}, receive_msg},
+           {send, #ssh_msg_newkeys{}},
+           {match, disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR), receive_msg}]},
+         {ssh_msg_unexpected_dh_gex, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           %% dh_alg is expected but dh_gex_alg is provided
+	   {send, #ssh_msg_kex_dh_gex_request{min = 1000, n = 3000, max = 4000}},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]},
+         {wrong_role, "KEX strict violation",
+          [receive_hello,
+           {send, hello},
+           {send, ssh_msg_kexinit},
+           {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+           %% client should not send message below
+           {send, ?HARDCODED_KEXDH_REPLY},
+           {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}]}],
+    TestProcedure =
+        fun({Msg, _, P}) ->
+                ct:log(
+                  "==== START: ~p (duplicated ~p) Expected Fail ====~n~p",
+                  [?FUNCTION_NAME, Msg, P]),
+                P
+        end,
+    [kex_strict_helper(Config, TestProcedure(Procedure), Reason) ||
+        Procedure = {_, Reason, _} <- TestFlows],
+    ct:log("==== END ====="),
+    ok.
+
+kex_strict_violation_2(Config) ->
+    ExpectedReason = "KEX strict violation",
+    {ok, TestRef} = ssh_test_lib:add_log_handler(),
+    Level = ssh_test_lib:get_log_level(),
+    ssh_test_lib:set_log_level(debug),
+    %% Connect and negotiate keys
+    {ok, InitialState} = ssh_trpt_test_lib:exec(
+                           [{set_options, [print_ops, print_seqnums, print_messages]}]),
+    {ok, UpToUnexpectedKexDHReply} =
+        ssh_trpt_test_lib:exec(
+          [{connect,
+            server_host(Config),server_port(Config),
+            [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                    {cipher,?DEFAULT_CIPHERS}
+                                   ]},
+             {silently_accept_hosts, true},
+             {recv_ext_info, false},
+             {user_dir, user_dir(Config)},
+             {user_interaction, false}
+            | proplists:get_value(extra_options,Config,[])
+            ]}] ++
+              [receive_hello,
+               {send, hello},
+               {send, ssh_msg_kexinit},
+               {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+               {send, ssh_msg_kexdh_init},
+               {match, #ssh_msg_kexdh_reply{_='_'}, receive_msg},
+               %% client should not send message below
+               {send, ?HARDCODED_KEXDH_REPLY},
+               {match, {'or', [#ssh_msg_newkeys{_='_'},
+                               disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED)]},
+                receive_msg}],
+          InitialState),
+    case ssh_trpt_test_lib:return_value(UpToUnexpectedKexDHReply) of
+        {ssh_msg_newkeys} ->
+            ct:log("1st flow - extra match for disconnect needed"),
+            ssh_trpt_test_lib:exec(
+              [{match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}],
+              UpToUnexpectedKexDHReply);
+        _ ->
+            ct:log("2nd flow disconnect already received")
+    end,
+    ct:sleep(100),
+    {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+    ssh_test_lib:rm_log_handler(),
+    ct:log("Events = ~p", [Events]),
+    true = ssh_test_lib:kex_strict_negotiated(client, Events),
+    true = ssh_test_lib:kex_strict_negotiated(server, Events),
+    true = ssh_test_lib:event_logged(server, Events, ExpectedReason),
+    ssh_test_lib:set_log_level(Level),
+    ok.
+
+%% Connect to an erlang server and inject unexpected non-SSH binary
+kex_strict_msg_unknown(Config) ->
+    ct:log("START: ~p~n=================================", [?FUNCTION_NAME]),
+    ExpectedReason = "Bad packet: Size",
+    TestMessages =
+        [receive_hello,
+         {send, hello},
+         {send, ssh_msg_kexinit},
+         {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+         {send, ssh_msg_kexdh_init},
+         {send, ssh_msg_unknown},
+         {match, #ssh_msg_kexdh_reply{_='_'}, receive_msg},
+         {match, disconnect(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED), receive_msg}],
+    kex_strict_helper(Config, TestMessages, ExpectedReason).
+
+kex_strict_helper(Config, TestMessages, ExpectedReason) ->
+    {ok, TestRef} = ssh_test_lib:add_log_handler(),
+    Level = ssh_test_lib:get_log_level(),
+    ssh_test_lib:set_log_level(debug),
+    %% Connect and negotiate keys
+    {ok, InitialState} = ssh_trpt_test_lib:exec(
+                           [{set_options, [print_ops, print_seqnums, print_messages]}]),
+    {ok, _AfterKexState} =
+        ssh_trpt_test_lib:exec(
+          [{connect,
+            server_host(Config),server_port(Config),
+            [{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                    {cipher,?DEFAULT_CIPHERS}
+                                   ]},
+             {silently_accept_hosts, true},
+             {recv_ext_info, false},
+             {user_dir, user_dir(Config)},
+             {user_interaction, false}
+            | proplists:get_value(extra_options,Config,[])
+            ]}] ++
+              TestMessages,
+          InitialState),
+    ct:sleep(100),
+    {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+    ssh_test_lib:rm_log_handler(),
+    ct:log("Events = ~p", [Events]),
+    true = ssh_test_lib:kex_strict_negotiated(client, Events),
+    true = ssh_test_lib:kex_strict_negotiated(server, Events),
+    true = ssh_test_lib:event_logged(server, Events, ExpectedReason),
+    ssh_test_lib:set_log_level(Level),
+    ok.
 
 %%%----------------------------------------------------------------
 %%%
@@ -839,7 +1250,7 @@ modify_append(Config) ->
     Ciphers = filter_supported(cipher, ?CIPHERS),
     {ok,_} =
         chk_pref_algs(Config,
-                      [?DEFAULT_KEX, ?EXTRA_KEX],
+                      [?DEFAULT_KEX, ?EXTRA_KEX, list_to_atom(?kex_strict_s)],
                       Ciphers,
                       [{preferred_algorithms, [{kex,[?DEFAULT_KEX]},
                                                {cipher,Ciphers}
@@ -853,7 +1264,7 @@ modify_prepend(Config) ->
     Ciphers = filter_supported(cipher, ?CIPHERS),
     {ok,_} =
         chk_pref_algs(Config,
-                      [?EXTRA_KEX, ?DEFAULT_KEX],
+                      [?EXTRA_KEX, ?DEFAULT_KEX, list_to_atom(?kex_strict_s)],
                       Ciphers,
                       [{preferred_algorithms, [{kex,[?DEFAULT_KEX]},
                                                {cipher,Ciphers}
@@ -867,7 +1278,7 @@ modify_rm(Config) ->
     Ciphers = filter_supported(cipher, ?CIPHERS),
     {ok,_} =
         chk_pref_algs(Config,
-                      [?DEFAULT_KEX],
+                      [?DEFAULT_KEX, list_to_atom(?kex_strict_s)],
                       tl(Ciphers),
                       [{preferred_algorithms, [{kex,[?DEFAULT_KEX,?EXTRA_KEX]},
                                                {cipher,Ciphers}
@@ -886,7 +1297,7 @@ modify_combo(Config) ->
     LastC = lists:last(Ciphers),
     {ok,_} =
         chk_pref_algs(Config,
-                      [?DEFAULT_KEX],
+                      [?DEFAULT_KEX, list_to_atom(?kex_strict_s)],
                       [LastC] ++ (tl(Ciphers)--[LastC]) ++ [hd(Ciphers)],
                       [{preferred_algorithms, [{kex,[?DEFAULT_KEX,?EXTRA_KEX]},
                                                {cipher,Ciphers}
@@ -903,8 +1314,6 @@ modify_combo(Config) ->
 
 %%%----------------------------------------------------------------
 %%%
-client_close_after_hello() -> [{timetrap,{seconds,80}}].
-
 client_close_after_hello(Config0) ->
     MaxSessions = 20,
     SleepSec = 15,
@@ -913,7 +1322,7 @@ client_close_after_hello(Config0) ->
                                         {negotiation_timeout,SleepSec*1000}
                                        ]),
 
-    {Parents0, Conns0, []} = find_handshake_parent(server_port(Config)),
+    {_Parents0, Conns0, []} = find_handshake_parent(server_port(Config)),
 
     Cs =
         [ssh_trpt_test_lib:exec(
@@ -975,6 +1384,44 @@ client_close_after_hello(Config0) ->
             {fail, no_handshakers}
     end.
 
+%%% Connect to an erlang server and pretend client sending extra
+%%% ssh_msg_service_request (Paramiko client behavior)
+extra_ssh_msg_service_request(Config) ->
+    %% Connect and negotiate keys
+    {ok,InitialState} = ssh_trpt_test_lib:exec(
+			  [{set_options, [print_ops, print_seqnums, print_messages]}]
+			 ),
+    {ok,AfterKexState} = connect_and_kex(Config, InitialState),
+    %% Do the authentcation
+    {User,Pwd} = server_user_password(Config),
+    UserAuthFlow =
+        fun(P) ->
+                [{send, #ssh_msg_service_request{name = "ssh-userauth"}},
+                 {match, #ssh_msg_service_accept{name = "ssh-userauth"}, receive_msg},
+                 {send, #ssh_msg_userauth_request{user = User,
+                                                  service = "ssh-connection",
+                                                  method = "password",
+                                                  data = <<?BOOLEAN(?FALSE),
+                                                           ?STRING(unicode:characters_to_binary(P))>>
+                                                 }}]
+        end,
+    {ok,EndState} =
+	ssh_trpt_test_lib:exec(
+          UserAuthFlow("WRONG") ++
+              [{match, #ssh_msg_userauth_failure{_='_'}, receive_msg}] ++
+              UserAuthFlow(Pwd) ++
+              [{match, #ssh_msg_userauth_success{_='_'}, receive_msg}],
+          AfterKexState),
+    %% Disconnect
+    {ok,_} =
+	ssh_trpt_test_lib:exec(
+	  [{send, #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+				      description = "End of the fun",
+				      language = ""
+				     }},
+	   close_socket
+	  ], EndState),
+    ok.
 
 %%%================================================================
 %%%==== Internal functions ========================================
@@ -984,7 +1431,7 @@ chk_pref_algs(Config,
               ExpectedKex,
               ExpectedCiphers,
               ServerPrefOpts) ->
-    %% Start the dameon
+    %% Start the daemon
     case ssh_test_lib:daemon(
                       [{send_ext_info,false},
                        {recv_ext_info,false},
@@ -1103,9 +1550,10 @@ std_connect({Host,Port}, Config, Opts) -
 std_connect(Host, Port, Config, Opts) ->
     {User,Pwd} = server_user_password(Config),
     ssh:connect(Host, Port, 
-		%% Prefere User's Opts to the default opts
+		%% Prefer User's Opts to the default opts
 		[O || O = {Tag,_} <- [{user,User},{password,Pwd},
 				      {silently_accept_hosts, true},
+                                      {save_accepted_host, false},
 				      {user_dir, user_dir(Config)},
 				      {user_interaction, false}],
 		      not lists:keymember(Tag, 1, Opts)
@@ -1140,6 +1588,84 @@ connect_and_kex(Config, InitialState) ->
       ],
       InitialState).
 
+channel_close_timeout(Config) ->
+    {User,_Pwd} = server_user_password(Config),
+    %% Create a listening socket as server socket:
+    {ok,InitialState} = ssh_trpt_test_lib:exec(listen),
+    HostPort = ssh_trpt_test_lib:server_host_port(InitialState),
+    %% Start a process handling one connection on the server side:
+    spawn_link(
+      fun() ->
+	      {ok,_} =
+		  ssh_trpt_test_lib:exec(
+		    [{set_options, [print_ops, print_messages]},
+		     {accept, [{system_dir, system_dir(Config)},
+			       {user_dir, user_dir(Config)},
+                               {idle_time, 50000}]},
+		     receive_hello,
+		     {send, hello},
+		     {send, ssh_msg_kexinit},
+		     {match, #ssh_msg_kexinit{_='_'}, receive_msg},
+		     {match, #ssh_msg_kexdh_init{_='_'}, receive_msg},
+		     {send, ssh_msg_kexdh_reply},
+		     {send, #ssh_msg_newkeys{}},
+		     {match,  #ssh_msg_newkeys{_='_'}, receive_msg},
+		     {match, #ssh_msg_service_request{name="ssh-userauth"}, receive_msg},
+		     {send, #ssh_msg_service_accept{name="ssh-userauth"}},
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="none",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_failure{authentications = "password",
+						      partial_success = false}},
+		     {match, #ssh_msg_userauth_request{service="ssh-connection",
+						       method="password",
+						       user=User,
+						       _='_'}, receive_msg},
+		     {send, #ssh_msg_userauth_success{}},
+                     {match, #ssh_msg_channel_open{channel_type="session",
+                                                   sender_channel=0,
+                                                   _='_'}, receive_msg},
+		     {send, #ssh_msg_channel_open_confirmation{recipient_channel= 0,
+                                                               sender_channel = 0,
+                                                               initial_window_size = 64*1024,
+                                                               maximum_packet_size = 32*1024
+                                                               }},
+                     {match, #ssh_msg_channel_open{channel_type="session",
+                                                   sender_channel=1,
+                                                   _='_'}, receive_msg},
+		     {send, #ssh_msg_channel_open_confirmation{recipient_channel= 1,
+                                                               sender_channel = 1,
+                                                               initial_window_size = 64*1024,
+                                                               maximum_packet_size = 32*1024}},
+                     {match, #ssh_msg_channel_close{recipient_channel = 0}, receive_msg},
+                     {match, disconnect(), receive_msg},
+		     print_state],
+		    InitialState)
+      end),
+    %% connect to it with a regular Erlang SSH client:
+    ChannelCloseTimeout = 3000,
+    {ok, ConnRef} = std_connect(HostPort, Config,
+				[{preferred_algorithms,[{kex,[?DEFAULT_KEX]},
+                                                        {cipher,?DEFAULT_CIPHERS}
+                                                       ]},
+                                 {channel_close_timeout, ChannelCloseTimeout},
+                                 {idle_time, 50000}
+                                ]
+			       ),
+    {ok,  Channel0} = ssh_connection:session_channel(ConnRef, 50000),
+    {ok, _Channel1} = ssh_connection:session_channel(ConnRef, 50000),
+    %% Close the channel from client side, the server does not reply with 'channel-close'
+    %% After the timeout, the client should drop the cache entry
+    _ = ssh_connection:close(ConnRef, Channel0),
+    receive
+    after ChannelCloseTimeout + 1000 ->
+        {channels, Channels} = ssh:connection_info(ConnRef, channels),
+        ct:log("Channel entries ~p", [Channels]),
+        %% Only one channel entry should be present, the other one should be dropped
+        1 = length(Channels),
+        ssh:close(ConnRef)
+    end.
 %%%----------------------------------------------------------------
 
 %%% For matching peer disconnection
@@ -1159,29 +1685,34 @@ find_handshake_parent(Port) ->
     find_handshake_parent(supervisor:which_children(sshd_sup), Port, Acc).
 
 
-find_handshake_parent([{{server,ssh_system_sup,_,Port,default},
+find_handshake_parent([{{ssh_system_sup,{address,_,Port,_}},
                         Pid,supervisor, [ssh_system_sup]}|_],
                       Port, Acc) ->
     find_handshake_parent(supervisor:which_children(Pid), Port, Acc);
 
-find_handshake_parent([{{ssh_acceptor_sup,_,Port,default},
+find_handshake_parent([{{ssh_acceptor_sup,{address,_,Port,_}},
                         PidS,supervisor,[ssh_acceptor_sup]}|T],
                        Port, {AccP,AccC,AccH}) ->
     ParentHandshakers =
-        [{PidW,PidH} || {{ssh_acceptor_sup,_,Port1,default}, PidW, worker, [ssh_acceptor]} <- supervisor:which_children(PidS),
-                        Port1 == Port,
-                        PidH <- element(2, process_info(PidW,links)),
-                        is_pid(PidH),
-                        process_info(PidH,current_function) == {current_function,{ssh_connection_handler,handshake,3}}],
+        [{PidW,PidH} ||
+            {{ssh_acceptor_sup,{address,_,Port1,_}}, PidW, worker,
+             [ssh_acceptor]} <- supervisor:which_children(PidS),
+            Port1 == Port,
+            PidH <- element(2, process_info(PidW,links)),
+            is_pid(PidH),
+            process_info(PidH,current_function) ==
+                {current_function,
+                 {ssh_connection_handler,handshake,4}}],
     {Parents,Handshakers} = lists:unzip(ParentHandshakers),
     find_handshake_parent(T, Port, {AccP++Parents, AccC, AccH++Handshakers});
 
-find_handshake_parent([{_Ref,PidS,supervisor,[ssh_subsystem_sup]}|T], Port, {AccP,AccC,AccH}) ->
+find_handshake_parent([{_Ref,PidS,supervisor,[ssh_connection_sup]}|T],
+                      Port, {AccP,AccC,AccH}) ->
     Connections =
-        [P || {{server,ssh_connection_sup,_,Port1}, Pid, supervisor, [ssh_connection_sup]} <- supervisor:which_children(PidS),
-              Port == Port1,
-              {undefined,P,worker,[ssh_connection_handler]} <- supervisor:which_children(Pid)],
-     find_handshake_parent(T, Port, {AccP, AccC++Connections, AccH});
+        [Pid ||
+            {connection,Pid,worker,[ssh_connection_handler]} <-
+                supervisor:which_children(PidS)],
+    find_handshake_parent(T, Port, {AccP, AccC++Connections, AccH});
 
 find_handshake_parent([_|T], Port, Acc) ->
     find_handshake_parent(T, Port, Acc);
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_sftpd_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2006-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
          open_file_dir_v6/1,
          read_dir/1,
          read_file/1,
+         max_path/1,
          real_path/1,
          relative_path/1,
          relpath/1,
@@ -51,7 +52,6 @@
          retrieve_attributes/1,
          root_with_cwd/1,
          set_attributes/1,
-         sshd_read_file/1,
          ver3_open_flags/1,
          ver3_rename/1,
          ver6_basic/1,
@@ -60,6 +60,7 @@
 
 -include_lib("common_test/include/ct.hrl").
 -include_lib("kernel/include/file.hrl").
+-include_lib("stdlib/include/assert.hrl").
 -include("ssh_xfer.hrl").
 -include("ssh.hrl").
 -include("ssh_test_lib.hrl").
@@ -71,9 +72,9 @@
 -define(SSH_TIMEOUT, 10000).
 -define(REG_ATTERS, <<0,0,0,0,1>>).
 -define(UNIX_EPOCH,  62167219200).
-
--define(is_set(F, Bits),
-	((F) band (Bits)) == (F)).
+-define(MAX_HANDLES, 10).
+-define(MAX_PATH, 200).
+-define(is_set(F, Bits), ((F) band (Bits)) == (F)).
 
 %%--------------------------------------------------------------------
 %% Common Test interface functions -----------------------------------
@@ -86,6 +87,7 @@ all() ->
     [open_close_file, 
      open_close_dir, 
      read_file, 
+     max_path,
      read_dir,
      write_file, 
      rename_file, 
@@ -97,8 +99,7 @@ all() ->
      links,
      ver3_rename,
      ver3_open_flags,
-     relpath, 
-     sshd_read_file,
+     relpath,
      ver6_basic,
      access_outside_root,
      root_with_cwd,
@@ -180,7 +181,9 @@ init_per_testcase(TestCase, Config) ->
 								  {sftpd_vsn, 6}])],
 			  ssh:daemon(0, [{subsystems, SubSystems}|Options]);
 		      _ ->
-			  SubSystems = [ssh_sftpd:subsystem_spec([])],
+			  SubSystems = [ssh_sftpd:subsystem_spec(
+                                          [{max_handles, ?MAX_HANDLES},
+					  {max_path, ?MAX_PATH}])],
 			  ssh:daemon(0, [{subsystems, SubSystems}|Options])
 		  end,
 
@@ -316,33 +319,62 @@ open_close_dir(Config) when is_list(Conf
 read_file(Config) when is_list(Config) ->
     PrivDir =  proplists:get_value(priv_dir, Config),
     FileName = filename:join(PrivDir, "test.txt"),
+         {Cm, Channel} = proplists:get_value(sftp, Config),
+    [begin
+         R1 = req_id(),
+         {ok, <<?SSH_FXP_HANDLE, ?UINT32(R1), Handle/binary>>, _} =
+             open_file(FileName, Cm, Channel, R1, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+                       ?SSH_FXF_OPEN_EXISTING),
+         R2 = req_id(),
+         {ok, <<?SSH_FXP_DATA, ?UINT32(R2), ?UINT32(_Length), Data/binary>>, _} =
+             read_file(Handle, 100, 0, Cm, Channel, R2),
+         {ok, Data} = file:read_file(FileName)
+     end || _I <- lists:seq(0, ?MAX_HANDLES-1)],
+    ReqId = req_id(),
+    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_FAILURE),
+           ?UINT32(MsgLen), Msg:MsgLen/binary,
+           ?UINT32(LangTagLen), _LangTag:LangTagLen/binary>>, _} =
+        open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES,
+                  ?SSH_FXF_OPEN_EXISTING),
+    ct:log("Message: ~s", [Msg]),
+    ok.
 
-    ReqId = 0,
+%%--------------------------------------------------------------------
+max_path(Config) when is_list(Config) ->
+    PrivDir =  proplists:get_value(priv_dir, Config),
+    FileName = filename:join(PrivDir, "test.txt"),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
-	open_file(FileName, Cm, Channel, ReqId,
+    %% verify max_path limit
+    LongFileName =
+        filename:join(PrivDir,
+                      "t" ++ lists:flatten(lists:duplicate(?MAX_PATH, "e")) ++ "st.txt"),
+    {ok, _} = file:copy(FileName, LongFileName),
+    ReqId1 = req_id(),
+    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId1), ?UINT32(?SSH_FX_NO_SUCH_PATH),
+	  _/binary>>, _} =
+	open_file(LongFileName, Cm, Channel, ReqId1,
 		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-
-    NewReqId = 1,
-
-    {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length),
-	  Data/binary>>, _} =
-	read_file(Handle, 100, 0, Cm, Channel, NewReqId),
-
-    {ok, Data} = file:read_file(FileName).
+		  ?SSH_FXF_OPEN_EXISTING).
 
 %%--------------------------------------------------------------------
 read_dir(Config) when is_list(Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-    ReqId = 0,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
-	open_dir(PrivDir, Cm, Channel, ReqId),
-    ok = read_dir(Handle, Cm, Channel, ReqId).
+    [begin
+         R1 = req_id(),
+         {ok, <<?SSH_FXP_HANDLE, ?UINT32(R1), Handle/binary>>, _} =
+             open_dir(PrivDir, Cm, Channel, R1),
+         R2 = req_id(),
+         ok = read_dir(Handle, Cm, Channel, R2)
+     end || _I <- lists:seq(0, ?MAX_HANDLES-1)],
+    ReqId = req_id(),
+    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_FAILURE),
+           ?UINT32(MsgLen), Msg:MsgLen/binary,
+           ?UINT32(LangTagLen), _LangTag:LangTagLen/binary>>, _} =
+        open_dir(PrivDir, Cm, Channel, ReqId),
+    ct:log("Message: ~s", [Msg]),
+    ok.
 
-%%--------------------------------------------------------------------
 write_file(Config) when is_list(Config) ->
     PrivDir =  proplists:get_value(priv_dir, Config),
     FileName = filename:join(PrivDir, "test.txt"),
@@ -388,35 +420,33 @@ rename_file(Config) when is_list(Config)
     PrivDir =  proplists:get_value(priv_dir, Config),
     FileName = filename:join(PrivDir, "test.txt"),
     NewFileName = filename:join(PrivDir, "test1.txt"),
-    ReqId = 0,
+    LongFileName =
+        filename:join(PrivDir,
+                      "t" ++ lists:flatten(lists:duplicate(?MAX_PATH, "e")) ++ "st.txt"),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId),
-	  ?UINT32(?SSH_FX_OK), _/binary>>, _} =
-	rename(FileName, NewFileName, Cm, Channel, ReqId, 6, 0),
-
-    NewReqId = ReqId + 1,
-
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId),
-	  ?UINT32(?SSH_FX_OK), _/binary>>, _} =
-	rename(NewFileName, FileName, Cm, Channel, NewReqId, 6,
-	       ?SSH_FXP_RENAME_OVERWRITE),
-
-    NewReqId1 = NewReqId + 1,
-    file:copy(FileName, NewFileName),
-
-    %% No owerwrite
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1),
-	  ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} =
-	rename(FileName, NewFileName, Cm, Channel, NewReqId1, 6,
-	       ?SSH_FXP_RENAME_NATIVE),
-
-    NewReqId2 = NewReqId1 + 1,
-
-    {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2),
-	  ?UINT32(?SSH_FX_OP_UNSUPPORTED), _/binary>>, _} =
-	rename(FileName, NewFileName, Cm, Channel, NewReqId2, 6,
-	       ?SSH_FXP_RENAME_ATOMIC).
+    Version = 6,
+    [begin
+         case Action of
+             {Code, AFile, BFile, Flags} ->
+                 ReqId = req_id(),
+                 ct:log("ReqId = ~p,~nCode = ~p,~nAFile = ~p,~nBFile = ~p,~nFlags = ~p",
+                        [ReqId, Code, AFile, BFile, Flags]),
+                 {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(Code), _/binary>>, _} =
+                     rename(AFile, BFile, Cm, Channel, ReqId, Version, Flags);
+             {file_copy, AFile, BFile} ->
+                 {ok, _} = file:copy(AFile, BFile)
+         end
+     end ||
+        Action <-
+            [{?SSH_FX_OK, FileName, NewFileName, 0},
+             {?SSH_FX_OK, NewFileName, FileName, ?SSH_FXP_RENAME_OVERWRITE},
+             {file_copy, FileName, NewFileName},
+             %% no overwrite
+             {?SSH_FX_FILE_ALREADY_EXISTS, FileName, NewFileName, ?SSH_FXP_RENAME_NATIVE},
+             {?SSH_FX_OP_UNSUPPORTED, FileName, NewFileName, ?SSH_FXP_RENAME_ATOMIC},
+             %% max_path
+             {?SSH_FX_NO_SUCH_PATH, FileName, LongFileName, 0}]],
+    ok.
 
 %%--------------------------------------------------------------------
 mk_rm_dir(Config) when is_list(Config) ->
@@ -644,27 +674,6 @@ relpath(Config) when is_list(Config) ->
 	    Root = Path
     end.
 
-%%--------------------------------------------------------------------
-sshd_read_file(Config) when is_list(Config) ->
-    PrivDir =  proplists:get_value(priv_dir, Config),
-    FileName = filename:join(PrivDir, "test.txt"),
-
-    ReqId = 0,
-    {Cm, Channel} = proplists:get_value(sftp, Config),
-
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
-	open_file(FileName, Cm, Channel, ReqId,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-
-    NewReqId = 1,
-
-    {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length),
-	  Data/binary>>, _} =
-	read_file(Handle, 100, 0, Cm, Channel, NewReqId),
-
-    {ok, Data} = file:read_file(FileName).
-%%--------------------------------------------------------------------
 ver6_basic(Config) when is_list(Config) ->
     PrivDir =  proplists:get_value(priv_dir, Config),
     %FileName = filename:join(PrivDir, "test.txt"),
@@ -685,7 +694,7 @@ access_outside_root(Config) when is_list
     BadFilePath = filename:join([BaseDir, bad]),
     ok = file:write_file(BadFilePath, <<>>),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-    %% Try to access a file parallell to the RootDir:
+    %% Try to access a file parallel to the RootDir:
     try_access("/../bad",   Cm, Channel, 0),
     %% Try to access the same file via the CWD which is /b relative to the RootDir:
     try_access("../../bad", Cm, Channel, 1).
@@ -717,7 +726,7 @@ try_access(Path, Cm, Channel, ReqId) ->
                     end
             end;
         _ ->
-            ct:fail("Completly unexpected return: ~p", [Return])
+            ct:fail("Completely unexpected return: ~p", [Return])
     end.
 
 %%--------------------------------------------------------------------
@@ -728,25 +737,33 @@ root_with_cwd(Config) when is_list(Confi
     FileName = "root_with_cwd.txt",
     FilePath = filename:join(CWD, FileName),
     ok = filelib:ensure_dir(FilePath),
-    ok = file:write_file(FilePath ++ "0", <<>>),
-    ok = file:write_file(FilePath ++ "1", <<>>),
-    ok = file:write_file(FilePath ++ "2", <<>>),
     {Cm, Channel} = proplists:get_value(sftp, Config),
-    ReqId0 = 0,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId0), _Handle0/binary>>, _} =
-	open_file(FileName ++ "0", Cm, Channel, ReqId0,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-    ReqId1 = 1,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId1), _Handle1/binary>>, _} =
-	open_file("./" ++ FileName ++ "1", Cm, Channel, ReqId1,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING),
-    ReqId2 = 2,
-    {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId2), _Handle2/binary>>, _} =
-	open_file("/home/" ++ FileName ++ "2", Cm, Channel, ReqId2,
-		  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
-		  ?SSH_FXF_OPEN_EXISTING).
+
+    %% repeat procedure to make sure uniq file handles are generated
+    FileHandles =
+        [begin
+             ReqIdStr = integer_to_list(ReqId),
+             ok = file:write_file(FilePath ++ ReqIdStr, <<>>),
+             {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} =
+                 open_file(FileName ++ ReqIdStr, Cm, Channel, ReqId,
+                           ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
+                           ?SSH_FXF_OPEN_EXISTING),
+             Handle
+         end ||
+            ReqId <- lists:seq(0,2)],
+    ?assertEqual(length(FileHandles),
+                 length(lists:uniq(FileHandles))),
+    %% create a gap in file handles
+    [_, MiddleHandle, _] = FileHandles,
+    close(MiddleHandle, 3, Cm, Channel),
+
+    %% check that gap in file handles is is re-used
+    GapReqId = 4,
+    {ok, <<?SSH_FXP_HANDLE, ?UINT32(GapReqId), MiddleHandle/binary>>, _} =
+        open_file(FileName ++ integer_to_list(1), Cm, Channel, GapReqId,
+                  ?ACE4_READ_DATA  bor ?ACE4_READ_ATTRIBUTES,
+                  ?SSH_FXF_OPEN_EXISTING),
+    ok.
 
 %%--------------------------------------------------------------------
 relative_path(Config) when is_list(Config) ->
@@ -1078,3 +1095,12 @@ encode_file_type(Type) ->
 
 not_default_permissions() ->
     8#600. %% User read-write-only
+
+req_id() ->
+    ReqId =
+        case get(req_id) of
+            undefined -> 0;
+            I -> I
+        end,
+    put(req_id, ReqId + 1),
+    ReqId.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_sftp_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_sftp_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -1,7 +1,7 @@
 %
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@
          pos_read/1,
          pos_write/1,
          position/1,
+         read_6GB/1,
          read_crypto_tar/1,
          read_dir/1,
          read_file/1,
@@ -81,8 +82,10 @@
 -include_lib("common_test/include/ct.hrl").
 -include_lib("kernel/include/file.hrl").
 -include("ssh_test_lib.hrl").
-						% Default timetrap timeout
--define(default_timeout, ?t:minutes(1)).
+-include_lib("stdlib/include/assert.hrl").
+
+%% Default timetrap timeout
+-define(default_timeout, test_server:minutes(1)).
 
 %%--------------------------------------------------------------------
 %% Common Test interface functions -----------------------------------
@@ -119,7 +122,9 @@ groups() ->
 
      {unicode, [], [{group,erlang_server},
 		    {group,openssh_server},
-		    sftp_nonexistent_subsystem]},
+                    read_6GB,
+		    sftp_nonexistent_subsystem
+                   ]},
 
      {big_recvpkt_size, [], [{group,erlang_server},
 			     {group,openssh_server}]},
@@ -227,22 +232,7 @@ init_per_group(erlang_server, Config) ->
     [{peer, {fmt_host(HostX),PortX}}, {group, erlang_server}, {sftpd, Sftpd} | Config];
 
 init_per_group(openssh_server, Config) ->
-    ct:comment("Begin ~p",[grps(Config)]),
-    Host = ssh_test_lib:hostname(),
-    case (catch ssh_sftp:start_channel(Host,
-				       [{user_interaction, false},
-					{silently_accept_hosts, true}])) of
-	{ok, _ChannelPid, Connection} ->
-	    [{peer, {_HostName,{IPx,Portx}}}] = ssh:connection_info(Connection,[peer]),
-	    ssh:close(Connection),
-	    [{w2l, fun w2l/1},
-             {peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
-	{error,"Key exchange failed"} ->
-	    {skip, "openssh server doesn't support the tested kex algorithm"};
-	Other ->
-            ct:log("No openssh server. Cause:~n~p~n",[Other]),
-	    {skip, "No openssh daemon (see log in testcase)"} 
-    end;
+    verify_openssh(Config);
 
 init_per_group(remote_tar, Config) ->
     ct:comment("Begin ~p",[grps(Config)]),
@@ -250,16 +240,16 @@ init_per_group(remote_tar, Config) ->
     ct:log("Server (~p) at ~p:~p",[proplists:get_value(group,Config),Host,Port]),
     User = proplists:get_value(user, Config),
     Passwd = proplists:get_value(passwd, Config),
-    {ok, Connection} =
+    Connection =
 	case proplists:get_value(group, Config) of
 	    erlang_server ->
-		ssh:connect(Host, Port,
+		ssh_test_lib:connect(Host, Port,
 			    [{user, User},
 			     {password, Passwd},
 			     {user_interaction, false},
 			     {silently_accept_hosts, true}]);
 	    openssh_server ->
-		ssh:connect(Host, Port,
+		ssh_test_lib:connect(Host, Port,
 			    [{user_interaction, false},
 			     {silently_accept_hosts, true}])
 	end,
@@ -284,7 +274,18 @@ end_per_group(_, Config) ->
     Config.
 
 %%--------------------------------------------------------------------
-
+init_per_testcase(read_6GB, Config) ->
+    case verify_openssh(Config) of
+        Result = {skip, _} ->
+            Result;
+        _ ->
+            case os:type() of
+                {win32, _} ->
+                    {skip, "/dev/zero not available on Windws"};
+                _ ->
+                    init_per_testcase(read_6GB_prepare_openssh_server, Config)
+            end
+    end;
 init_per_testcase(sftp_nonexistent_subsystem, Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     SysDir =  proplists:get_value(data_dir, Config),
@@ -297,7 +298,6 @@ init_per_testcase(sftp_nonexistent_subsy
 				  [{User, Passwd}]}
 				]),
     [{sftpd, Sftpd} | Config];
-
 init_per_testcase(version_option, Config0) ->
     Config = prepare(Config0),
     TmpConfig0 = lists:keydelete(watchdog, 1, Config),
@@ -312,10 +312,11 @@ init_per_testcase(version_option, Config
 				{user, User},
 				{password, Passwd},
 				{user_interaction, false},
-				{silently_accept_hosts, true}]),
+				{silently_accept_hosts, true},
+                                {save_accepted_host, false}
+                               ]),
     Sftp = {ChannelPid, Connection},
     [{sftp,Sftp}, {watchdog, Dog} | TmpConfig];
-
 init_per_testcase(Case, Config00) ->
     Config0 = prepare(Config00),
     Config1 = lists:keydelete(watchdog, 1, Config0),
@@ -327,16 +328,30 @@ init_per_testcase(Case, Config00) ->
 		   undefined -> [];
 		   Sz -> [{packet_size,Sz}]
 	       end,
+    PrepareOpenSSHServer =
+        fun() ->
+                Host = ssh_test_lib:hostname(),
+        	{ok, ChannelPid, Connection} =
+        	    ssh_sftp:start_channel(Host,
+        				   [{user_interaction, false},
+        				    {silently_accept_hosts, true},
+                                            {save_accepted_host, false}
+                                           | PktSzOpt
+        				   ]),
+        	Sftp = {ChannelPid, Connection},
+        	[{sftp, Sftp}, {watchdog, Dog} | Config2]
+        end,
     Config =
 	case proplists:get_value(group,Config2) of
 	    erlang_server ->
-		{_,Host, Port} =  proplists:get_value(sftpd, Config2),
-		{ok, ChannelPid, Connection}  = 
+		{_,Host, Port} = proplists:get_value(sftpd, Config2),
+		{ok, ChannelPid, Connection} =
 		    ssh_sftp:start_channel(Host, Port,
 					   [{user, User},
 					    {password, Passwd},
 					    {user_interaction, false},
-					    {silently_accept_hosts, true}
+					    {silently_accept_hosts, true},
+                                            {save_accepted_host, false}
 					    | PktSzOpt
 					   ]
 					  ),
@@ -345,17 +360,10 @@ init_per_testcase(Case, Config00) ->
 	    openssh_server when Case == links ->
 		{skip, "known bug in openssh"};
 	    openssh_server ->
-		Host = ssh_test_lib:hostname(),
-		{ok, ChannelPid, Connection} = 
-		    ssh_sftp:start_channel(Host, 
-					   [{user_interaction, false},
-					    {silently_accept_hosts, true}
-					    | PktSzOpt
-					   ]),
-		Sftp = {ChannelPid, Connection},
-		[{sftp, Sftp}, {watchdog, Dog} | Config2]
+                PrepareOpenSSHServer();
+            _ when Case == read_6GB_prepare_openssh_server ->
+                PrepareOpenSSHServer()
 	end,
-
     case catch proplists:get_value(remote_tar,Config) of
 	%% The 'catch' is for the case of Config={skip,...}
 	true ->
@@ -556,26 +564,62 @@ links(Config) when is_list(Config) ->
 retrieve_attributes(Config) when is_list(Config) ->
     FileName = proplists:get_value(filename, Config),
     SftpFileName = w2l(Config, FileName),
-
     {Sftp, _} = proplists:get_value(sftp, Config),
     {ok, FileInfo} = ssh_sftp:read_file_info(Sftp, SftpFileName),
     {ok, NewFileInfo} = file:read_file_info(FileName),
-
-    %% TODO comparison. There are some differences now is that ok?
-    ct:log("SFTP: ~p   FILE: ~p~n", [FileInfo, NewFileInfo]).
+    ct:log("ssh_sftp:read_file_info(~p): ~p~n"
+           "file:read_file_info(~p): ~p",
+           [SftpFileName, FileInfo, FileName, NewFileInfo]),
+    {ExpectedUid, ExpectedGid} =
+        case {os:type(), proplists:get_value(group,Config)} of
+            {{win32, _}, openssh_server} ->
+                %% Windows compiled Erlang is expected will return 0;
+                %% but when Erlang(Windows) client interacts with
+                %% OpenSSH server - value 1000 is received by client
+                %% over SFTP (because OpenSSH is compiled for Linux
+                %% and runs on WSL)
+                {1000, 1000};
+            _ ->
+                {FileInfo#file_info.uid, FileInfo#file_info.gid}
+        end,
+    ?assertEqual(ExpectedUid, NewFileInfo#file_info.uid),
+    ?assertEqual(ExpectedGid, NewFileInfo#file_info.gid),
+    ok.
 
 %%--------------------------------------------------------------------
 set_attributes(Config) when is_list(Config) ->
     FileName = proplists:get_value(testfile, Config),
     SftpFileName = w2l(Config, FileName),
-
     {Sftp, _} = proplists:get_value(sftp, Config),
     {ok,Fd} = file:open(FileName, write),
     io:put_chars(Fd,"foo"),
-    ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#400}),
-    {error, eacces} = file:write_file(FileName, "hello again"),
-    ok = ssh_sftp:write_file_info(Sftp, SftpFileName, #file_info{mode=8#600}),
-    ok = file:write_file(FileName, "hello again").
+    TestWriting =
+        fun(FInfo) ->
+                ok = ssh_sftp:write_file_info(Sftp, SftpFileName,
+                                              FInfo#file_info{mode=8#400}),
+                {error, eacces} = file:write_file(FileName, "hello again"),
+                ok = ssh_sftp:write_file_info(Sftp, SftpFileName,
+                                              FInfo#file_info{mode=8#600}),
+                ok = file:write_file(FileName, "hello again")
+        end,
+    TestWriting(#file_info{}),
+    IsErlangServer =
+        fun() ->
+                TcGroupPath = proplists:get_value(tc_group_path, Config),
+                {_, Path} = lists:unzip(lists:flatten(TcGroupPath)),
+                lists:member(erlang_server, Path)
+        end,
+    case IsErlangServer() of
+        true ->
+            ct:log("Testing with writing a complete #file_info record"),
+            {ok, FileInfo} = file:read_file_info(SftpFileName),
+            TestWriting(FileInfo);
+        _ ->
+            %% with OpenSSH daemon started by other user above instruction end
+            %% up with permission denied
+            ok
+    end,
+    ok.
 
 %%--------------------------------------------------------------------
 file_owner_access(Config) when is_list(Config) ->
@@ -669,6 +713,29 @@ position(Config) when is_list(Config) ->
     {ok, 1} = ssh_sftp:position(Sftp, Handle, cur),
     {ok, "2"} = ssh_sftp:read(Sftp, Handle, 1).
 
+read_6GB(Config) when is_list(Config) ->
+    ct:timetrap(16*?default_timeout),
+    FileName = "/dev/zero",
+    SftpFileName = w2l(Config, FileName),
+    {SftpChannel, _ConnectionRef} = proplists:get_value(sftp, Config),
+    ChunkSize = 65535,
+    N = 100000,
+    {ok, Handle} = ssh_sftp:open(SftpChannel, SftpFileName, [read]),
+    ExpectedList = lists:duplicate(ChunkSize, 0),
+    [begin
+         MBTransferred = io_lib:format("~.2f", [I * ChunkSize / 1048576.0]),
+         case ssh_sftp:read(SftpChannel, Handle, ChunkSize, timer:minutes(1)) of
+             {ok, ExpectedList} ->
+                 [ct:log("~n~s MB read~n", [MBTransferred]) || I rem 10000 == 0];
+             Result ->
+                 ct:log("## After reading ~s MB~n## Unexpected result received = ~p",
+                        [MBTransferred, Result]),
+                 ct:fail(unexpected_reason)
+         end
+     end ||
+        I <- lists:seq(0, N)],
+    ok.
+
 %%--------------------------------------------------------------------
 pos_read(Config) when is_list(Config) ->
     FileName = proplists:get_value(testfile, Config),
@@ -736,7 +803,8 @@ start_channel_sock(Config) ->
 	end,
 
     Opts = [{user_interaction, false},
-	    {silently_accept_hosts, true}
+	    {silently_accept_hosts, true},
+            {save_accepted_host, false}
 	    | LoginOpts],
 
     {Host,Port} = proplists:get_value(peer, Config),
@@ -775,7 +843,7 @@ start_channel_sock(Config) ->
     %% Test that the socket is closed when the Connection closes
     ok = ssh:close(Conn),
     timer:sleep(400), %% Until the stop sequence is fixed
-    {error,einval} = inet:getopts(Sock, [active]),
+    {error,_} = inet:getopts(Sock, [active]),
 
     ok.
 
@@ -789,7 +857,9 @@ sftp_nonexistent_subsystem(Config) when
 			       [{user_interaction, false},
 				{user, User},
 				{password, Passwd},
-				{silently_accept_hosts, true}]).
+				{silently_accept_hosts, true},
+                                {save_accepted_host, false}
+                               ]).
 
 %%--------------------------------------------------------------------
 version_option(Config) when is_list(Config) ->
@@ -1224,4 +1294,22 @@ w2l(Config, P) ->
     W2L = proplists:get_value(w2l, Config, fun(X) -> X end),
     W2L(P).
 
-    
+verify_openssh(Config) ->
+    ct:comment("Begin ~p",[grps(Config)]),
+    Host = ssh_test_lib:hostname(),
+    case (catch ssh_sftp:start_channel(Host,
+				       [{user_interaction, false},
+					{silently_accept_hosts, true},
+                                        {save_accepted_host, false}
+                                       ])) of
+	{ok, _ChannelPid, Connection} ->
+	    [{peer, {_HostName,{IPx,Portx}}}] = ssh:connection_info(Connection,[peer]),
+	    ssh:close(Connection),
+	    [{w2l, fun w2l/1},
+             {peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
+	{error,"Key exchange failed"} ->
+	    {skip, "openssh server doesn't support the tested kex algorithm"};
+	Other ->
+            ct:log("No openssh server. Cause:~n~p~n",[Other]),
+	    {skip, "No openssh daemon (see log in testcase)"}
+    end.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_sup_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_sup_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_sup_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2015-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -24,8 +24,7 @@
 -include("ssh.hrl").
 -include("ssh_test_lib.hrl").
 
--export([
-         suite/0,
+-export([suite/0,
          all/0,
          groups/0,
          init_per_suite/1,
@@ -33,38 +32,42 @@
          init_per_group/2,
          end_per_group/2,
          init_per_testcase/2,
-         end_per_testcase/2
-        ]).
+         end_per_testcase/2]).
 
--export([
-         default_tree/1,
+-export([default_tree/1,
          killed_acceptor_restarts/1,
          shell_channel_tree/1,
          sshc_subtree/1,
          sshd_subtree/1,
-         sshd_subtree_profile/1
-        ]).
+         sshd_subtree_profile/1]).
 
 -define(USER, "Alladin").
 -define(PASSWD, "Sesame").
 
 -define(WAIT_FOR_SHUTDOWN, 500).
 
+-define(SSHC_SUP(Pid), {sshc_sup, Pid, supervisor, [supervisor]}).
+-define(SSHD_SUP(Pid), {sshd_sup, Pid, supervisor, [supervisor]}).
+-define(SYSTEM_SUP(Pid,Address),
+        {{ssh_system_sup, Address}, Pid, supervisor,[ssh_system_sup]}).
+-define(CONNECTION_SUP(Pid), {_,Pid, supervisor,[ssh_connection_sup]}).
+-define(ACCEPTOR_SUP(Pid,Address),
+        {{ssh_acceptor_sup,Address},Pid,supervisor,[ssh_acceptor_sup]}).
+-define(ACCEPTOR_WORKER(Pid,Address),
+        {{ssh_acceptor_sup,Address},Pid,worker,[ssh_acceptor]}).
+
 %%--------------------------------------------------------------------
 %% Common Test interface functions -----------------------------------
 %%--------------------------------------------------------------------
-
 suite() ->
     [{ct_hooks,[ts_install_cth]},
      {timetrap,{seconds,100}}].
 
-all() -> 
+all() ->
     [default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile,
-     killed_acceptor_restarts,
-     shell_channel_tree
-    ].
+     killed_acceptor_restarts, shell_channel_tree].
 
-groups() -> 
+groups() ->
     [].
 
 init_per_group(_GroupName, Config) ->
@@ -85,73 +88,67 @@ init_per_suite(Config) ->
 end_per_suite(_) ->
     ok.
 
-init_per_testcase(sshc_subtree, Config) ->  
+init_per_testcase(sshc_subtree, Config) ->
     ssh:start(),
     SystemDir = proplists:get_value(data_dir, Config),
     {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
-					      {failfun, fun ssh_test_lib:failfun/2},
-					      {user_passwords,
-					       [{?USER, ?PASSWD}]}]),
+                                             {failfun, fun ssh_test_lib:failfun/2},
+                                             {user_passwords,
+                                              [{?USER, ?PASSWD}]}]),
     [{server, {Pid, Host, Port}} | Config];
 init_per_testcase(Case, Config) ->
     end_per_testcase(Case, Config),
     ssh:start(),
     Config.
 end_per_testcase(sshc_subtree, Config) ->
-    {Pid,_,_} = proplists:get_value(server, Config), 
+    {Pid,_,_} = proplists:get_value(server, Config),
     ssh:stop_daemon(Pid),
     ssh:stop();
 end_per_testcase(_, _Config) ->
     ssh:stop().
 
 %%-------------------------------------------------------------------------
-%% Test cases 
+%% Test cases
 %%-------------------------------------------------------------------------
 default_tree(Config) when is_list(Config) ->
     TopSupChildren = supervisor:which_children(ssh_sup),
     2 = length(TopSupChildren),
-    {value, {sshc_sup, _, supervisor,[sshc_sup]}} =
-	lists:keysearch(sshc_sup, 1, TopSupChildren),
-    {value, {sshd_sup, _,supervisor,[sshd_sup]}} = 
-	lists:keysearch(sshd_sup, 1, TopSupChildren),
-    ?wait_match([{client_controller,_,worker,_}], supervisor:which_children(sshc_sup)),
+    {value, ?SSHC_SUP(_)} = lists:keysearch(sshc_sup, 1, TopSupChildren),
+    {value, ?SSHD_SUP(_)} = lists:keysearch(sshd_sup, 1, TopSupChildren),
+    ?wait_match([], supervisor:which_children(sshc_sup)),
     ?wait_match([], supervisor:which_children(sshd_sup)).
 
 %%-------------------------------------------------------------------------
 sshc_subtree(Config) when is_list(Config) ->
     {_Pid, Host, Port} = proplists:get_value(server, Config),
     UserDir = proplists:get_value(userdir, Config),
-
-    ?wait_match([{client_controller,_,worker,_}], supervisor:which_children(sshc_sup)),
-
-    {ok, Pid1} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
-					  {user_interaction, false},
-					  {user, ?USER}, {password, ?PASSWD},{user_dir, UserDir}]),
-
-    ?wait_match([{{client,ssh_system_sup, LocalIP, LocalPort, ?DEFAULT_PROFILE},
-                  SysSup, supervisor,[ssh_system_sup]},
-                 {client_controller,_,worker,_}
-                ],
+    ?wait_match([], supervisor:which_children(sshc_sup)),
+    Pid1 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+                                             {save_accepted_host, false},
+                                             {user_interaction, false},
+                                             {user, ?USER},
+                                             {password, ?PASSWD},
+                                             {user_dir, UserDir}]),
+    ?wait_match([?CONNECTION_SUP(ConnectionSup)],
 		supervisor:which_children(sshc_sup),
-                [SysSup, LocalIP, LocalPort]),
-    check_sshc_system_tree(SysSup, Pid1, LocalIP, LocalPort, Config),
-
-    {ok, Pid2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
-					  {user_interaction, false},
-					  {user, ?USER}, {password, ?PASSWD}, {user_dir, UserDir}]),
-    ?wait_match([{_, _,supervisor,[ssh_system_sup]},
-                 {_, _,supervisor,[ssh_system_sup]},
-                 {client_controller,_,worker,_}
+                [ConnectionSup]),
+    check_sshc_system_tree(ConnectionSup, Pid1, Config),
+    Pid2 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+                                             {save_accepted_host, false},
+                                             {user_interaction, false},
+                                             {user, ?USER},
+                                             {password, ?PASSWD},
+                                             {user_dir, UserDir}]),
+    ?wait_match([?CONNECTION_SUP(_),
+                 ?CONNECTION_SUP(_)
                 ],
 		supervisor:which_children(sshc_sup)),
-
     ssh:close(Pid1),
-    ?wait_match([{_, _,supervisor,[ssh_system_sup]},
-                 {client_controller,_,worker,_}
+    ?wait_match([?CONNECTION_SUP(_)
                 ],
 		supervisor:which_children(sshc_sup)),
     ssh:close(Pid2),
-    ?wait_match([{client_controller,_,worker,_}], supervisor:which_children(sshc_sup)).
+    ?wait_match([], supervisor:which_children(sshc_sup)).
 
 %%-------------------------------------------------------------------------
 sshd_subtree(Config) when is_list(Config) ->
@@ -160,11 +157,10 @@ sshd_subtree(Config) when is_list(Config
                                                   {failfun, fun ssh_test_lib:failfun/2},
                                                   {user_passwords,
                                                    [{?USER, ?PASSWD}]}]),
-
     ct:log("Expect HostIP=~p, Port=~p, Daemon=~p",[HostIP,Port,Daemon]),
-    ?wait_match([{{server,ssh_system_sup, ListenIP, Port, ?DEFAULT_PROFILE},
-		  Daemon, supervisor,
-		  [ssh_system_sup]}],
+    ?wait_match([?SYSTEM_SUP(Daemon, #address{address=ListenIP,
+                                              port=Port,
+                                              profile=?DEFAULT_PROFILE})],
 		supervisor:which_children(sshd_sup),
 		[ListenIP,Daemon]),
     true = ssh_test_lib:match_ip(HostIP, ListenIP),
@@ -175,18 +171,17 @@ sshd_subtree(Config) when is_list(Config
 
 %%-------------------------------------------------------------------------
 sshd_subtree_profile(Config) when is_list(Config) ->
-    Profile = proplists:get_value(profile, Config), 
+    Profile = proplists:get_value(profile, Config),
     SystemDir = proplists:get_value(data_dir, Config),
-
     {Daemon, HostIP, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
                                                   {failfun, fun ssh_test_lib:failfun/2},
                                                   {user_passwords,
                                                    [{?USER, ?PASSWD}]},
                                                   {profile, Profile}]),
     ct:log("Expect HostIP=~p, Port=~p, Profile=~p, Daemon=~p",[HostIP,Port,Profile,Daemon]),
-    ?wait_match([{{server,ssh_system_sup, ListenIP,Port,Profile},
-		  Daemon, supervisor,
-		  [ssh_system_sup]}],
+    ?wait_match([?SYSTEM_SUP(Daemon, #address{address=ListenIP,
+                                              port=Port,
+                                              profile=Profile})],
 		supervisor:which_children(sshd_sup),
 		[ListenIP,Daemon]),
     true = ssh_test_lib:match_ip(HostIP, ListenIP),
@@ -197,7 +192,7 @@ sshd_subtree_profile(Config) when is_lis
 
 %%-------------------------------------------------------------------------
 killed_acceptor_restarts(Config) ->
-    Profile = proplists:get_value(profile, Config), 
+    Profile = proplists:get_value(profile, Config),
     SystemDir = proplists:get_value(data_dir, Config),
     UserDir = proplists:get_value(userdir, Config),
     {ok, DaemonPid} = ssh:daemon(0, [{system_dir, SystemDir},
@@ -220,7 +215,8 @@ killed_acceptor_restarts(Config) ->
     true = (AccPid /= AccPid2),
 
     %% Connect first client and check it is alive:
-    {ok,C1} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
+    C1 = ssh_test_lib:connect("localhost", Port, [{silently_accept_hosts, true},
+                                                   {save_accepted_host, false},
                                               {user_interaction, false},
                                               {user, ?USER},
                                               {password, ?PASSWD},
@@ -247,6 +243,7 @@ killed_acceptor_restarts(Config) ->
     %% Connect second client and check it is alive:
     C2 =
         case ssh:connect("localhost", Port, [{silently_accept_hosts, true},
+                                             {save_accepted_host, false},
                                              {user_interaction, false},
                                              {user, ?USER},
                                              {password, ?PASSWD},
@@ -259,17 +256,17 @@ killed_acceptor_restarts(Config) ->
         end,
 
     [{client_version,_}] = ssh:connection_info(C2,[client_version]),
-    
+
     ct:log("~s",[lists:flatten(ssh_info:string())]),
 
     %% Check first client is still alive:
     [{client_version,_}] = ssh:connection_info(C1,[client_version]),
-    
+
     ok = ssh:stop_daemon(DaemonPid2),
     ?wait_match(undefined, process_info(DaemonPid2), 1000, 30),
     [{client_version,_}] = ssh:connection_info(C1,[client_version]),
     [{client_version,_}] = ssh:connection_info(C2,[client_version]),
-    
+
     ok = ssh:stop_daemon(DaemonPid),
     ?wait_match(undefined, process_info(DaemonPid), 1000, 30),
     ?wait_match({error,closed}, ssh:connection_info(C1,[client_version]), 1000, 5),
@@ -301,25 +298,36 @@ shell_channel_tree(Config) ->
 						      {user_interaction, true},
 						      {user_dir, UserDir}]),
 
-    [ChannelSup|_] = Sups0 = chk_empty_con_daemon(Daemon),
-    
+    [ConnectionSup,_ChPid|_] = Sups0 = chk_empty_con_daemon(Daemon),
+
     {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
     ok = ssh_connection:shell(ConnectionRef,ChannelId0),
     success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId0, [{pty_opts,[{onlcr,1}]}]),
 
-    ?wait_match([{_, GroupPid,worker,[ssh_server_channel]}],
-		supervisor:which_children(ChannelSup),
-               [GroupPid]),
+    ?wait_match([{_,_, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,ChSup,supervisor,[ssh_channel_sup]},
+                 {connection,_,worker,[ssh_connection_handler]}
+                ],
+		supervisor:which_children(ConnectionSup),
+                [ChSup]),
+    ?wait_match([{_,GroupPid,worker,[ssh_server_channel]}
+                ],
+                supervisor:which_children(ChSup),
+                [GroupPid]),
+
+
     {links,GroupLinks} = erlang:process_info(GroupPid, links),
-    [ShellPid] = GroupLinks--[ChannelSup],
+    ct:log("GroupPid = ~p, GroupLinks = ~p Sups0 = ~p",[GroupPid,GroupLinks,Sups0]),
+    [ShellPid] = GroupLinks--[ChSup],
     ct:log("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]),
 
     receive
-        {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!\r\n">>}} ->
+        {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!",Rest/binary>>}} ->
+            ct:log("TimeoutShell started. Rest = ~p", [Rest]),
             receive
-                %%---- wait for the subsystem to terminate
+                %%---- wait for the connection to terminate
                 {ssh_cm,ConnectionRef,{closed,ChannelId0}} ->
-                    ct:log("Subsystem terminated",[]),
+                    ct:log("Connection terminated",[]),
                     case {chk_empty_con_daemon(Daemon),
                           process_info(GroupPid),
                           process_info(ShellPid)} of
@@ -337,123 +345,137 @@ shell_channel_tree(Config) ->
                             ct:fail("Sup tree changed!")
                     end
             after 10000 ->
+                    ct:log("~p:~p  Flush unexpected: ~p", [?MODULE,?LINE,flush_rest()]),
                     ssh:close(ConnectionRef),
                     ssh:stop_daemon(Daemon),
-                    ct:fail("CLI Timeout")
+                    ct:fail("CLI Timeout 1")
             end
     after 10000 ->
+            ct:log("~p:~p  Flush unexpected: ~p", [?MODULE,?LINE,flush_rest()]),
             ssh:close(ConnectionRef),
             ssh:stop_daemon(Daemon),
-            ct:fail("CLI Timeout")
+            ct:fail("CLI Timeout 2")
     end.
 
-
 chk_empty_con_daemon(Daemon) ->
-    ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
-		 {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
+    ?wait_match([?CONNECTION_SUP(ConnectionSup),
+		 ?ACCEPTOR_SUP(AccSup,_)
+                ],
 		supervisor:which_children(Daemon),
-                [SubSysSup,AccSup]),
-    ?wait_match([{_,_, supervisor,
-                  [ssh_tcpip_forward_acceptor_sup]},
-                 {{server,ssh_connection_sup, _,_},
-		  ConnectionSup, supervisor,
-		  [ssh_connection_sup]},
-		 {{server,ssh_channel_sup,_ ,_},
-		  ChannelSup,supervisor,
-		  [ssh_channel_sup]}],
-		supervisor:which_children(SubSysSup),
-		[ConnectionSup,ChannelSup]),
-    ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
-		supervisor:which_children(AccSup)),
-    ?wait_match([{_, _, worker,[ssh_connection_handler]}],
-		supervisor:which_children(ConnectionSup)),
-    ?wait_match([], supervisor:which_children(ChannelSup)),
-    [ChannelSup, ConnectionSup, SubSysSup, AccSup].
+                [ConnectionSup,AccSup]),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,ChSup,supervisor,[ssh_channel_sup]},
+                 {connection,ServerConnPid,worker,[ssh_connection_handler]}
+                ],
+		supervisor:which_children(ConnectionSup),
+		[ChSup,FwdAccSup,ServerConnPid]),
+    ?wait_match([], supervisor:which_children(FwdAccSup)),
+    ?wait_match([], supervisor:which_children(ChSup)),
+    ?wait_match([?ACCEPTOR_WORKER(_,_)],
+                supervisor:which_children(AccSup),
+                []),
+    [ConnectionSup, ChSup, ServerConnPid, AccSup, FwdAccSup].
 
 %%-------------------------------------------------------------------------
 %% Help functions
 %%-------------------------------------------------------------------------
-check_sshd_system_tree(Daemon, Host, Port, Config) -> 
+check_sshd_system_tree(Daemon, Host, Port, Config) ->
     UserDir = proplists:get_value(userdir, Config),
-    {ok, Client} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
-                                            {user_interaction, false},
-                                            {user, ?USER},
-                                            {password, ?PASSWD},
-                                            {user_dir, UserDir}]),
-    
-    ?wait_match([{_,SubSysSup, supervisor,[ssh_subsystem_sup]},
-		 {{ssh_acceptor_sup,_,_,_}, AccSup, supervisor,[ssh_acceptor_sup]}],
+    ClientConn = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+                                                   {user_interaction, false},
+                                                   {user, ?USER},
+                                                   {password, ?PASSWD},
+                                                   {user_dir, UserDir}]),
+    ?wait_match([?CONNECTION_SUP(ConnectionSup),
+		 ?ACCEPTOR_SUP(AccSup,_)],
 		supervisor:which_children(Daemon),
-                [SubSysSup,AccSup]),
-    
-    ?wait_match([{_,
-                  _AcceptorSup, supervisor,
-                  [ssh_tcpip_forward_acceptor_sup]},
-                 {{server,ssh_connection_sup, _,_},
-		  ConnectionSup, supervisor,
-		  [ssh_connection_sup]},
-		 {{server,ssh_channel_sup,_ ,_},
-		  ChannelSup,supervisor,
-		  [ssh_channel_sup]}],
-		supervisor:which_children(SubSysSup),
-		[ConnectionSup,ChannelSup]),
-    
-    ?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
-		supervisor:which_children(AccSup)),
-    
-    ?wait_match([{_, _, worker,[ssh_connection_handler]}],
-		supervisor:which_children(ConnectionSup)),
-    
-    ?wait_match([], supervisor:which_children(ChannelSup)),
-    
-    {ok,PidC} = ssh_sftp:start_channel(Client),
+                [ConnectionSup,AccSup]),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,_,supervisor,[ssh_channel_sup]},
+                 {connection,ServerConn,worker,[ssh_connection_handler]}],
+		supervisor:which_children(ConnectionSup),
+		[FwdAccSup,ServerConn]),
+    ?wait_match([], supervisor:which_children(FwdAccSup)),
+    ?wait_match([?ACCEPTOR_WORKER(_,_)], supervisor:which_children(AccSup)),
+    {ok,PidC} = ssh_sftp:start_channel(ClientConn),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,ChSup,supervisor,[ssh_channel_sup]},
+                 {connection,ServerConn,worker,[ssh_connection_handler]}],
+		supervisor:which_children(ConnectionSup),
+		[ChSup,ServerConn]),
 
-    ?wait_match([{_, PidS,worker,[ssh_server_channel]}],
-		supervisor:which_children(ChannelSup),
+    ?wait_match([{_,PidS,worker,[ssh_server_channel]}],
+                supervisor:which_children(ChSup),
                 [PidS]),
     true = (PidS =/= PidC),
-
-    ssh:close(Client).
+    ?wait_match([], supervisor:which_children(FwdAccSup)),
+    ssh:close(ClientConn).
 
 
-check_sshc_system_tree(SysSup, Connection, LocalIP, LocalPort, _Config) ->
-    ?wait_match([{_,SubSysSup,supervisor,[ssh_subsystem_sup]}],
-                supervisor:which_children(SysSup),
-                [SubSysSup]),
-    ?wait_match([{_,FwdAccSup, supervisor,
-                  [ssh_tcpip_forward_acceptor_sup]},
-                 {{client,ssh_connection_sup, LocalIP, LocalPort},
-		  ConnectionSup, supervisor,
-		  [ssh_connection_sup]},
-		 {{client,ssh_channel_sup, LocalIP, LocalPort},
-		  ChannelSup,supervisor,
-		  [ssh_channel_sup]}],
-		supervisor:which_children(SubSysSup),
-		[ConnectionSup,ChannelSup,FwdAccSup]),
-    ?wait_match([{_, Connection, worker,[ssh_connection_handler]}],
-		supervisor:which_children(ConnectionSup)),
-    ?wait_match([], supervisor:which_children(ChannelSup)),
+check_sshc_system_tree(ConnectionSup, Connection, _Config) ->
+    ?wait_match([?CONNECTION_SUP(ConnectionSup)],
+                supervisor:which_children(sshc_sup),
+                [ConnectionSup]),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,_,supervisor,[ssh_channel_sup]},
+                 {connection,Connection,worker,[ssh_connection_handler]}
+                ],
+		supervisor:which_children(ConnectionSup),
+		[FwdAccSup]),
     ?wait_match([], supervisor:which_children(FwdAccSup)),
 
     {ok,ChPid1} = ssh_sftp:start_channel(Connection),
-    ?wait_match([{_,ChPid1,worker,[ssh_client_channel]}],
-                supervisor:which_children(ChannelSup)),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,ChSup,supervisor, [ssh_channel_sup]},
+                 {connection,Connection,worker,[ssh_connection_handler]}
+                ],
+		supervisor:which_children(ConnectionSup),
+		[ChSup,FwdAccSup]),
+
+    ?wait_match([{_,ChPid1,worker,[ssh_client_channel]}
+                ],
+                supervisor:which_children(ChSup),
+                [ChPid1]),
 
     {ok,ChPid2} = ssh_sftp:start_channel(Connection),
-    ?wait_match([{_,ChPidA,worker,[ssh_client_channel]},
-                 {_,ChPidB,worker,[ssh_client_channel]}],
-                supervisor:which_children(ChannelSup),
-               [ChPidA, ChPidB]),
-    true = (lists:sort([ChPidA, ChPidB]) == lists:sort([ChPid1, ChPid2])),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,ChSup,supervisor, [ssh_channel_sup]},
+                 {connection,Connection,worker,[ssh_connection_handler]}
+                ],
+		supervisor:which_children(ConnectionSup),
+		[ChSup,FwdAccSup]),
+
+    ?wait_match([{_,ChPid2,worker,[ssh_client_channel]},
+                 {_,ChPid1,worker,[ssh_client_channel]}
+                ],
+                supervisor:which_children(ChSup),
+                [ChPid1,ChPid2]),
 
     ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid1]),
     exit(ChPid1, kill),
-    ?wait_match([{_,ChPid2,worker,[ssh_client_channel]}],
-                supervisor:which_children(ChannelSup)),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,ChSup,supervisor, [ssh_channel_sup]},
+                 {connection,Connection,worker,[ssh_connection_handler]}
+                ],
+		supervisor:which_children(ConnectionSup),
+		[ChSup,FwdAccSup]),
+
+    ?wait_match([{_,ChPid2,worker,[ssh_client_channel]}
+                ],
+                supervisor:which_children(ChSup),
+                [ChPid2]),
 
     ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid2]),
     exit(ChPid2, kill),
-    ?wait_match([], supervisor:which_children(ChannelSup)),
+    ?wait_match([{_,FwdAccSup, supervisor,[ssh_tcpip_forward_acceptor_sup]},
+                 {_,ChSup,supervisor, [ssh_channel_sup]},
+                 {connection,Connection,worker,[ssh_connection_handler]}
+                ],
+		supervisor:which_children(ConnectionSup),
+		[ChSup,FwdAccSup]),
+
+    ?wait_match([], supervisor:which_children(ChSup)),
+
     ct:pal("... now there should not be any SUPERVISOR REPORT.~n", []).
 
 
@@ -464,21 +486,17 @@ acceptor_pid(DaemonPid) ->
                         Parent ! {self(), supsearch,
                                   [{AccPid,ListenAddr,Port}
 
-                                   || {{server,ssh_system_sup,ListenAddr,Port,NS},
-                                       DPid,supervisor,
-                                       [ssh_system_sup]} <- supervisor:which_children(sshd_sup),
+                                   || ?SYSTEM_SUP(DPid,#address{address=ListenAddr,port=Port,profile=NS})
+                                          <- supervisor:which_children(sshd_sup),
                                       DPid == DaemonPid,
 
-                                      {{ssh_acceptor_sup,L1,P1,NS1},
-                                       AccSupPid,supervisor,
-                                       [ssh_acceptor_sup]} <- supervisor:which_children(DaemonPid),
-                                      L1 == ListenAddr,
-                                      P1 == Port,
-                                      NS1 == NS1,
-
-                                      {{ssh_acceptor_sup,L2,P2,NS2},
-                                       AccPid,worker,
-                                       [ssh_acceptor]} <- supervisor:which_children(AccSupPid),
+                                      ?ACCEPTOR_SUP(AccSupPid,_)
+                                          <- supervisor:which_children(DaemonPid),
+
+                                      ?ACCEPTOR_WORKER(AccPid, #address{address=L2,
+                                                                        port=P2,
+                                                                        profile=NS2})
+                                          <- supervisor:which_children(AccSupPid),
                                       L2 == ListenAddr,
                                       P2 == Port,
                                       NS2 == NS]}
@@ -487,3 +505,10 @@ acceptor_pid(DaemonPid) ->
     after 2000 -> timeout
     end.
 
+%%%----------------------------------------------------------------
+flush_rest() -> lists:reverse(flush_rest([])).
+
+flush_rest(Acc) ->
+    receive Any -> [Any|Acc]
+    after 0 -> Acc
+    end.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_test_lib.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_test_lib.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_test_lib.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ daemon/2,
 daemon/3,
 daemon_port/1,
 daemon_port/2,
+gen_tcp_connect/2,
 gen_tcp_connect/3,
 open_sshc/3,
 open_sshc/4,
@@ -63,6 +64,7 @@ del_dirs/1,
 del_dir_contents/1,
 do_del_files/2,
 openssh_sanity_check/1,
+verify_sanity_check/1,
 default_algorithms/1,
 default_algorithms/3,
 default_algorithms/2,
@@ -120,26 +122,70 @@ setup_host_key_create_dir/3,
 setup_host_key/3,
 setup_known_host/3,
 get_addr_str/0,
-file_base_name/2
+file_base_name/2,
+kex_strict_negotiated/2,
+event_logged/3
         ]).
+%% logger callbacks and related helpers
+-export([log/2,
+         get_log_level/0, set_log_level/1, add_log_handler/0,
+         rm_log_handler/0, get_log_events/1]).
 
 -include_lib("common_test/include/ct.hrl").
 -include("ssh_transport.hrl").
 -include_lib("kernel/include/file.hrl").
 -include("ssh_test_lib.hrl").
 
+-define(SANITY_CHECK_NOTE,
+        "For enabling test, make sure following commands work:~n"
+        "ok = ssh:start(), "
+        "{ok, _} = ssh:connect(\"localhost\", 22, "
+        "[{password,\"\"},{silently_accept_hosts, true}, "
+        "{save_accepted_host, false}, {user_interaction, false}]).").
+
 %%%----------------------------------------------------------------
 connect(Port, Options) when is_integer(Port) ->
     connect(hostname(), Port, Options).
 
 connect(any, Port, Options) ->
     connect(hostname(), Port, Options);
-connect(Host, Port, Options) ->
+
+connect(Host, ?SSH_DEFAULT_PORT, Options0) ->
+    Options =
+        set_opts_if_not_set([{silently_accept_hosts, true},
+                             {save_accepted_host, false},
+                             {user_interaction, false}
+                            ], Options0),
+    do_connect(Host, ?SSH_DEFAULT_PORT, Options);
+
+connect(Host, Port, Options0) ->
+    Options =
+        case proplists:get_value(user_dir,Options0) of
+            undefined ->
+                %% Avoid uppdating the known_hosts if it is the default one
+                set_opts_if_not_set([{save_accepted_host, false}], Options0);
+            _ ->
+                Options0
+        end,
+    do_connect(Host, Port, Options).
+
+
+do_connect(Host, Port, Options) ->
     R = ssh:connect(Host, Port, Options),
     ct:log("~p:~p ssh:connect(~p, ~p, ~p)~n -> ~p",[?MODULE,?LINE,Host, Port, Options, R]),
     {ok, ConnectionRef} = R,
     ConnectionRef.
 
+set_opts_if_not_set(OptsToSet, Options0) ->
+    lists:foldl(fun({K,V}, Opts) ->
+                        case proplists:get_value(K, Opts) of
+                            undefined ->
+                                [{K,V} | Opts];
+                            _ ->
+                                Opts
+                        end
+                end, Options0, OptsToSet).
+
 %%%----------------------------------------------------------------
 daemon(Options) ->
     daemon(any, 0, Options).
@@ -174,6 +220,9 @@ daemon_port(0, Pid) -> {ok,Dinf} = ssh:d
 daemon_port(Port, _) -> Port.
 
 %%%----------------------------------------------------------------
+gen_tcp_connect(Port, Options) ->
+    gen_tcp_connect("localhost", Port, Options).
+
 gen_tcp_connect(Host0, Port, Options) ->
     Host = ssh_test_lib:ntoa(ssh_test_lib:mangle_connect_address(Host0)),
     ct:log("~p:~p gen_tcp:connect(~p, ~p, ~p)~nHost0 = ~p",
@@ -244,8 +293,7 @@ std_simple_sftp(Host, Port, Config, Opts
     Data = crypto:strong_rand_bytes(proplists:get_value(std_simple_sftp_size,Config,10)),
     ok = ssh_sftp:write_file(ChannelRef, DataFile, Data),
     {ok,ReadData} = file:read_file(DataFile),
-    ok = ssh:close(ConnectionRef),
-    Data == ReadData.
+    {Data == ReadData, ConnectionRef}.
 
 %%%----------------------------------------------------------------
 std_simple_exec(Host, Port, Config) ->
@@ -284,7 +332,9 @@ start_shell(Port, IOServer, ExtraOptions
               ct:log("~p:~p:~p ssh_test_lib:start_shell(~p, ~p, ~p)",
                      [?MODULE,?LINE,self(), Port, IOServer, ExtraOptions]),
 	      Options = [{user_interaction, false},
-			 {silently_accept_hosts,true} | ExtraOptions],
+			 {silently_accept_hosts,true},
+                         {save_accepted_host,false}
+                         | ExtraOptions],
               try
                   group_leader(IOServer, self()),
                   case Port of
@@ -298,17 +348,17 @@ start_shell(Port, IOServer, ExtraOptions
                           ct:log("is_integer(Port) Call ssh:shell(~p, ~p, ~p)",
                                  [Host, Port, Options]),
                           ssh:shell(Host, Port, Options);
-                      Socket when is_port(Socket) ->
+                      ConnRef when is_pid(ConnRef) ->
+                          ct:log("is_pid(ConnRef) Call ssh:shell(~p)",
+                                 [ConnRef]),
+                          ssh:shell(ConnRef); % Options were given in ssh:connect
+                      Socket ->
                           receive
                               start -> ok
                           end,
-                          ct:log("is_port(Socket) Call ssh:shell(~p, ~p)",
+                          ct:log("Socket Call ssh:shell(~p, ~p)",
                                  [Socket, Options]),
-                          ssh:shell(Socket, Options);
-                      ConnRef when is_pid(ConnRef) ->
-                          ct:log("is_pid(ConnRef) Call ssh:shell(~p)",
-                                 [ConnRef]),
-                          ssh:shell(ConnRef) % Options were given in ssh:connect
+                          ssh:shell(Socket, Options)
                   end
               of
                   R ->
@@ -448,7 +498,7 @@ receive_exec_result(Msgs) when is_list(M
                             receive_exec_result(Msgs);
                         Other ->
                             ct:log("~p:~p unexpected Other ~p", [?MODULE,?FUNCTION_NAME,Other]),
-                            {unexpected_msg, Other}
+                            receive_exec_result(Msgs)
                     end
             end
     after 
@@ -526,20 +576,35 @@ do_del_files(Dir, Files) ->
                           end
                   end, Files).
 
-
 openssh_sanity_check(Config) ->
     ssh:start(),
-    case ssh:connect("localhost", 22, [{password,""},
-                                       {save_accepted_host, false},
-                                       {silently_accept_hosts, true} ]) of
+    case ssh:connect("localhost", ?SSH_DEFAULT_PORT,
+                     [{password,""},
+                      {silently_accept_hosts, true},
+                      {save_accepted_host, false},
+                      {user_interaction, false}
+                     ]) of
 	{ok, Pid} ->
 	    ssh:close(Pid),
 	    ssh:stop(),
-	    Config;
+	    [{sanity_check_result, ok} | Config];
 	Err ->
 	    Str = lists:append(io_lib:format("~p", [Err])),
+            ct:log("Error = ~p", [Err]),
+            ct:log(?SANITY_CHECK_NOTE),
 	    ssh:stop(),
-	    {skip, Str}
+	    [{sanity_check_result, Str} | Config]
+    end.
+
+verify_sanity_check(Config) ->
+    SanityCheckResult = proplists:get_value(sanity_check_result, Config, ok),
+    case SanityCheckResult of
+        ok ->
+            Config;
+        Err ->
+            ct:log("Error = ~p", [Err]),
+            ct:log(?SANITY_CHECK_NOTE),
+            {fail, passwordless_connection_failed}
     end.
 
 %%%--------------------------------------------------------------------
@@ -555,6 +620,7 @@ default_algorithms(sshd, Host, Port) ->
     try run_fake_ssh(
 	  ssh_trpt_test_lib:exec(
 	    [{connect,Host,Port, [{silently_accept_hosts, true},
+                                  {save_accepted_host, false},
 				  {user_interaction, false}]}]))
     catch
 	_C:_E ->
@@ -587,14 +653,14 @@ default_algorithms(sshc, DaemonOptions)
 	{hostport,Srvr,{_Host,Port}} ->
 	    spawn(fun()-> os:cmd(lists:concat(["ssh -o \"StrictHostKeyChecking no\" -p ",Port," localhost"])) end)
     after ?TIMEOUT ->
-	    ct:fail("No server respons (timeout) 1")
+	    ct:fail("No server response (timeout) 1")
     end,
 
     receive
 	{result,Srvr,L} ->
 	    L
     after ?TIMEOUT ->
-	    ct:fail("No server respons (timeout) 2")
+	    ct:fail("No server response (timeout) 2")
     end.
 
 run_fake_ssh({ok,InitialState}) ->
@@ -900,7 +966,7 @@ create_random_dir(Config) ->
 	    Name;
 	{error,eexist} ->
 	    %% The Name already denotes an existing file system object, try again.
-	    %% The likelyhood of always generating an existing file name is low
+	    %% The likelihood of always generating an existing file name is low
 	    create_random_dir(Config)
     end.
 
@@ -1227,3 +1293,83 @@ file_base_name(system_src, 'ecdsa-sha2-n
 file_base_name(system_src, Alg) -> file_base_name(system, Alg).
 
 %%%----------------------------------------------------------------
+-define(SEARCH_FUN(EXP),
+        begin
+            fun(#{msg := {string, EXP},
+                  level := debug}) ->
+                    true;
+               (_) ->
+                    false
+            end
+        end).
+-define(SEARCH_SUFFIX, " will use strict KEX ordering").
+
+kex_strict_negotiated(client, Events) ->
+    kex_strict_negotiated(?SEARCH_FUN("client" ++ ?SEARCH_SUFFIX), Events);
+kex_strict_negotiated(server, Events) ->
+    kex_strict_negotiated(?SEARCH_FUN("server" ++ ?SEARCH_SUFFIX), Events);
+kex_strict_negotiated(SearchFun, Events) when is_function(SearchFun) ->
+    %% FIXME use event_logged?
+    case lists:search(SearchFun, Events) of
+        {value, _} -> true;
+        _ -> false
+    end.
+
+event_logged(Role, Events, Reason) ->
+    SearchF =
+        fun(#{msg := {report, #{args := Args}}}) ->
+                AnyF = fun (E) when is_list(E) ->
+                               case string:find(E, Reason) of
+                                   nomatch -> false;
+                                   _ -> true
+                               end;
+                           (_) ->
+                               false
+                       end,
+                lists:member(Role, Args) andalso
+                    lists:any(AnyF, Args);
+           (_Event) ->
+                false
+        end,
+    case lists:search(SearchF, Events) of
+        {value, _} -> true;
+        _ -> false
+    end.
+
+get_log_level() ->
+    #{level := Level} = logger:get_primary_config(),
+    Level.
+
+set_log_level(Level) ->
+    ok = logger:set_primary_config(level, Level).
+
+add_log_handler() ->
+    logger:remove_handler(?MODULE),
+    TestRef = make_ref(),
+    ok = logger:add_handler(?MODULE, ?MODULE,
+                            #{level => debug,
+                              filter_default => log,
+                              recipient => self(),
+                              test_ref => TestRef}),
+    {ok, TestRef}.
+
+rm_log_handler() ->
+    ok = logger:remove_handler(?MODULE).
+
+get_log_events(TestRef) ->
+    {ok, get_log_events(TestRef, [])}.
+
+get_log_events(TestRef, Acc) ->
+    receive
+        {TestRef, Event} ->
+            get_log_events(TestRef, [Event | Acc])
+    after
+        500 ->
+            Acc
+    end.
+
+%% logger callbacks
+log(LogEvent = #{level:=_Level,msg:=_Msg,meta:=_Meta},
+    #{test_ref := TestRef, recipient := Recipient}) ->
+    Recipient ! {TestRef, LogEvent},
+    ok.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_to_openssh_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2024. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
 
 -include_lib("common_test/include/ct.hrl").
 -include("ssh_test_lib.hrl").
+-include("ssh_transport.hrl").
 
 -export([
          suite/0,
@@ -38,7 +39,9 @@
 
 -export([
          erlang_server_openssh_client_renegotiate/1,
+         eserver_oclient_kex_strict/1,
          erlang_shell_client_openssh_server/1,
+         eclient_oserver_kex_strict/1,
          exec_direct_with_io_in_sshc/1,
          exec_with_io_in_sshc/1,
          tunnel_in_erlclient_erlserver/1,
@@ -50,7 +53,6 @@
 
         ]).
 
--define(SSH_DEFAULT_PORT, 22).
 -define(REKEY_DATA_TMO, 65000).
 
 %%--------------------------------------------------------------------
@@ -66,7 +68,7 @@ all() ->
 	    {skip, "openSSH not installed on host"};
 	_ ->
 	    [{group, erlang_client},
-	     {group, erlang_server}
+             {group, erlang_server}
 	     ]
     end.
 
@@ -74,29 +76,31 @@ groups() ->
     [{erlang_client, [], [tunnel_in_erlclient_erlserver,
                           tunnel_out_erlclient_erlserver,
                           {group, tunnel_distro_server},
-                          erlang_shell_client_openssh_server
+                          erlang_shell_client_openssh_server,
+                          eclient_oserver_kex_strict
 			 ]},
      {tunnel_distro_server, [], [tunnel_in_erlclient_openssh_server,
                                  tunnel_out_erlclient_openssh_server]},
      {erlang_server, [], [{group, tunnel_distro_client},
                           erlang_server_openssh_client_renegotiate,
+                          eserver_oclient_kex_strict,
                           exec_with_io_in_sshc,
                           exec_direct_with_io_in_sshc
-			 ]},
+                         ]
+     },
      {tunnel_distro_client, [], [tunnel_in_non_erlclient_erlserver,
                                  tunnel_out_non_erlclient_erlserver]}
     ].
 
-init_per_suite(Config) ->
+init_per_suite(Config0) ->
     ?CHECK_CRYPTO(
-       case gen_tcp:connect("localhost", 22, []) of
+       case gen_tcp:connect("localhost", ?SSH_DEFAULT_PORT, [{active, false}]) of
 	   {error,econnrefused} ->
-	       {skip,"No openssh deamon (econnrefused)"};
-	   _ ->
+	       {skip,"No openssh daemon (econnrefused)"};
+	   {ok, Sock} ->
                ssh_test_lib:openssh_sanity_check(
-                 [{ptty_supported, ssh_test_lib:ptty_supported()}
-                  | Config]
-                )
+                 [{ptty_supported, ssh_test_lib:ptty_supported()},
+                  {kex_strict, check_kex_strict(Sock)}| Config0])
        end
       ).
 
@@ -107,7 +111,7 @@ init_per_group(erlang_server, Config) ->
     Config;
 init_per_group(G, Config) when G==tunnel_distro_server ;
                                G==tunnel_distro_client ->
-    case no_forwarding() of
+    case no_forwarding(Config) of
         true ->
             {skip, "port forwarding disabled in external ssh"};
         false ->
@@ -127,12 +131,15 @@ end_per_group(_, Config) ->
 
 init_per_testcase(erlang_server_openssh_client_renegotiate, Config) ->
     case os:type() of
-	{unix,_} -> ssh:start(), Config;
-	Type -> {skip, io_lib:format("Unsupported test on ~p",[Type])}
+	{unix,_} ->
+            ssh:start(),
+            ssh_test_lib:verify_sanity_check(Config);
+	Type ->
+            {skip, io_lib:format("Unsupported test on ~p",[Type])}
     end;
 init_per_testcase(_TestCase, Config) ->
     ssh:start(),
-    Config.
+    ssh_test_lib:verify_sanity_check(Config).
 
 end_per_testcase(_TestCase, _Config) ->
     ssh:stop(),
@@ -142,10 +149,32 @@ end_per_testcase(_TestCase, _Config) ->
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
 erlang_shell_client_openssh_server(Config) when is_list(Config) ->
+    eclient_oserver_helper2(eclient_oserver_helper1(), Config).
+
+eclient_oserver_kex_strict(Config) when is_list(Config)->
+    case proplists:get_value(kex_strict, Config) of
+        true ->
+            {ok, TestRef} = ssh_test_lib:add_log_handler(),
+            Level = ssh_test_lib:get_log_level(),
+            ssh_test_lib:set_log_level(debug),
+            HelperParams = eclient_oserver_helper1(),
+            {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+            true = ssh_test_lib:kex_strict_negotiated(client, Events),
+            ssh_test_lib:set_log_level(Level),
+            ssh_test_lib:rm_log_handler(),
+            eclient_oserver_helper2(HelperParams, Config);
+        _ ->
+            {skip, "KEX strict not support by local OpenSSH"}
+    end.
+
+eclient_oserver_helper1() ->
     process_flag(trap_exit, true),
     IO = ssh_test_lib:start_io_server(),
     Prev = lists:usort(supervisor:which_children(sshc_sup)),
     Shell = ssh_test_lib:start_shell(?SSH_DEFAULT_PORT, IO),
+    {Shell, Prev, IO}.
+
+eclient_oserver_helper2({Shell, Prev, IO}, Config) ->
     IO ! {input, self(), "echo Hej\n"},
     case proplists:get_value(ptty_supported, Config) of
         true ->
@@ -166,7 +195,6 @@ erlang_shell_client_openssh_server(Confi
                                  false
                          end)
     end.
-
 %%--------------------------------------------------------------------
 %% Test that the server could redirect stdin and stdout from/to an
 %% OpensSSH client when handling an exec request
@@ -176,11 +204,10 @@ exec_with_io_in_sshc(Config) when is_lis
                                              {failfun, fun ssh_test_lib:failfun/2}]),
     ct:sleep(500),
 
-    PrivDir = proplists:get_value(priv_dir, Config),
-    KnownHosts = filename:join(PrivDir, "known_hosts"),
+    _PrivDir = proplists:get_value(priv_dir, Config),
     ExecStr = "\"io:read('% ').\"",
     Cmd =  "echo howdy. | " ++ ssh_test_lib:open_sshc_cmd(Host, Port,
-                                                          [" -o UserKnownHostsFile=", KnownHosts,
+                                                          [" -o UserKnownHostsFile=", "/dev/null",
                                                            " -o CheckHostIP=no"
                                                            " -o StrictHostKeyChecking=no"
                                                            " -q"
@@ -210,10 +237,9 @@ exec_direct_with_io_in_sshc(Config) when
                                             ]),
     ct:sleep(500),
 
-    PrivDir = proplists:get_value(priv_dir, Config),
-    KnownHosts = filename:join(PrivDir, "known_hosts"),
+    _PrivDir = proplists:get_value(priv_dir, Config),
     Cmd =  "echo ciao. | " ++ ssh_test_lib:open_sshc_cmd(Host, Port,
-                                                          [" -o UserKnownHostsFile=", KnownHosts,
+                                                          [" -o UserKnownHostsFile=", "/dev/null",
                                                            " -o CheckHostIP=no"
                                                            " -o StrictHostKeyChecking=no"
                                                            " -q"
@@ -233,6 +259,28 @@ exec_direct_with_io_in_sshc(Config) when
 %%--------------------------------------------------------------------
 %% Test that the Erlang/OTP server can renegotiate with openSSH
 erlang_server_openssh_client_renegotiate(Config) ->
+    eserver_oclient_renegotiate_helper2(
+      eserver_oclient_renegotiate_helper1(Config)).
+
+eserver_oclient_kex_strict(Config) ->
+    case proplists:get_value(kex_strict, Config) of
+        true ->
+            {ok, TestRef} = ssh_test_lib:add_log_handler(),
+            Level = ssh_test_lib:get_log_level(),
+            ssh_test_lib:set_log_level(debug),
+
+            HelperParams = eserver_oclient_renegotiate_helper1(Config),
+            {ok, Events} = ssh_test_lib:get_log_events(TestRef),
+            ct:log("Events = ~n~p", [Events]),
+            true = ssh_test_lib:kex_strict_negotiated(server, Events),
+            ssh_test_lib:set_log_level(Level),
+            ssh_test_lib:rm_log_handler(),
+            eserver_oclient_renegotiate_helper2(HelperParams);
+        _ ->
+            {skip, "KEX strict not support by local OpenSSH"}
+    end.
+
+eserver_oclient_renegotiate_helper1(Config) ->
     _PubKeyAlg = ssh_rsa,
     SystemDir = proplists:get_value(data_dir, Config),
     PrivDir = proplists:get_value(priv_dir, Config),
@@ -246,9 +294,8 @@ erlang_server_openssh_client_renegotiate
     Data =  lists:duplicate(trunc(1.1*RenegLimitK*1024), $a),
     ok = file:write_file(DataFile, Data),
 
-    KnownHosts = filename:join(PrivDir, "known_hosts"),
     Cmd = ssh_test_lib:open_sshc_cmd(Host, Port,
-                                     [" -o UserKnownHostsFile=", KnownHosts,
+                                     [" -o UserKnownHostsFile=", "/dev/null",
                                       " -o CheckHostIP=no"
                                       " -o StrictHostKeyChecking=no"
                                       " -q"
@@ -257,10 +304,12 @@ erlang_server_openssh_client_renegotiate
 
 
     OpenSsh = ssh_test_lib:open_port({spawn, Cmd++" < "++DataFile}),
+    {Data, OpenSsh, Pid}.
 
-    Expect = fun({data,R}) -> 
+eserver_oclient_renegotiate_helper2({Data, OpenSsh, Pid}) ->
+    Expect = fun({data,R}) ->
 		     try
-			 NonAlphaChars = [C || C<-lists:seq(1,255), 
+			 NonAlphaChars = [C || C<-lists:seq(1,255),
 					       not lists:member(C,lists:seq($a,$z)),
 					       not lists:member(C,lists:seq($A,$Z))
 					 ],
@@ -278,21 +327,20 @@ erlang_server_openssh_client_renegotiate
 		(_) ->
 		     false
 	     end,
-    
-    try 
-	ssh_test_lib:rcv_expected(Expect, OpenSsh, ?TIMEOUT)
+    try
+        ssh_test_lib:rcv_expected(Expect, OpenSsh, ?TIMEOUT)
     of
-	_ ->
-	    %% Unfortunately we can't check that there has been a renegotiation, just trust OpenSSH.
-	    ssh:stop_daemon(Pid)
+        _ ->
+            %% Unfortunately we can't check that there has been a renegotiation, just trust OpenSSH.
+            ssh:stop_daemon(Pid)
     catch
-	throw:{skip,R} -> {skip,R}
+        throw:{skip,R} -> {skip,R}
     end.
 
 %%--------------------------------------------------------------------
 tunnel_out_non_erlclient_erlserver(Config) ->
     SystemDir = proplists:get_value(data_dir, Config),
-    PrivDir = proplists:get_value(priv_dir, Config),
+    _PrivDir = proplists:get_value(priv_dir, Config),
 
     {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_out, true},
                                              {system_dir, SystemDir},
@@ -302,9 +350,8 @@ tunnel_out_non_erlclient_erlserver(Confi
     ListenHost = {127,0,0,1},
     ListenPort = 2345,
 
-    KnownHosts = filename:join(PrivDir, "known_hosts"),
     Cmd = ssh_test_lib:open_sshc_cmd(Host, Port,
-                                     [" -o UserKnownHostsFile=", KnownHosts,
+                                     [" -o UserKnownHostsFile=", "/dev/null",
                                       " -o CheckHostIP=no"
                                       " -o StrictHostKeyChecking=no"
                                       " -q"
@@ -322,7 +369,7 @@ tunnel_out_non_erlclient_erlserver(Confi
 %%--------------------------------------------------------------------
 tunnel_in_non_erlclient_erlserver(Config) ->
     SystemDir = proplists:get_value(data_dir, Config),
-    UserDir = proplists:get_value(priv_dir, Config),
+    _UserDir = proplists:get_value(priv_dir, Config),
     {_Pid, Host, Port} = ssh_test_lib:daemon([{tcpip_tunnel_in, true},
                                               {system_dir, SystemDir},
                                               {failfun, fun ssh_test_lib:failfun/2}]),
@@ -331,10 +378,9 @@ tunnel_in_non_erlclient_erlserver(Config
     ListenHost = {127,0,0,1},
     ListenPort = 2345,
 
-    KnownHosts = filename:join(UserDir, "known_hosts"),
     Cmd =
         ssh_test_lib:open_sshc_cmd(Host, Port,
-                                   [" -o UserKnownHostsFile=", KnownHosts,
+                                   [" -o UserKnownHostsFile=", "/dev/null",
                                     " -o CheckHostIP=no"
                                     " -o StrictHostKeyChecking=no"
                                     " -q"
@@ -370,8 +416,7 @@ tunnel_in_erlclient_erlserver(Config) ->
 
 %%--------------------------------------------------------------------
 tunnel_in_erlclient_openssh_server(_Config) ->
-    C = ssh_test_lib:connect(loopback, 22, [{silently_accept_hosts, true},
-                                            {user_interaction, false}]),
+    C = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     {ToSock, ToHost, ToPort} = tunneling_listner(),
     
     ListenHost = {127,0,0,1},
@@ -401,8 +446,7 @@ tunnel_out_erlclient_erlserver(Config) -
 
 %%--------------------------------------------------------------------
 tunnel_out_erlclient_openssh_server(_Config) ->
-    C = ssh_test_lib:connect(loopback, 22, [{silently_accept_hosts, true},
-                                            {user_interaction, false}]),
+    C = ssh_test_lib:connect(?SSH_DEFAULT_PORT, []),
     {ToSock, ToHost, ToPort} = tunneling_listner(),
     
     ListenHost = {127,0,0,1},
@@ -535,9 +579,14 @@ extra_logout() ->
     end.
 
 %%%----------------------------------------------------------------
-no_forwarding() ->
+no_forwarding(Config) ->
     %%% Check if the ssh of the OS has tunneling enabled
-    Cmnd = "ssh -R 0:localhost:4567 localhost exit",
+    _UserDir = proplists:get_value(priv_dir, Config),
+    Cmnd = ["ssh "
+            " -o UserKnownHostsFile=", "/dev/null",
+            " -o CheckHostIP=no"
+            " -o StrictHostKeyChecking=no"
+            " -R 0:localhost:4567 localhost exit"],
     FailRegExp =
         "Port forwarding is disabled"
         "|remote port forwarding failed"
@@ -571,3 +620,18 @@ no_forwarding() ->
            "---- The function no_forwarding() returns ~p",
            [Cmnd,TheText, FailRegExp, Result]),
     Result.
+
+check_kex_strict(Sock) ->
+    %% Send some version, in order to receive KEXINIT from server
+    ok = gen_tcp:send(Sock, "SSH-2.0-OpenSSH_9.5\r\n"),
+    ct:sleep(100),
+    {ok, Packet} = gen_tcp:recv(Sock, 0),
+    case string:find(Packet, ?kex_strict_s) of
+        nomatch ->
+            ct:log("KEX strict NOT supported by local OpenSSH"),
+            false;
+        _ ->
+            ct:log("KEX strict supported by local OpenSSH"),
+            true
+    end.
+
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_trpt_test_lib.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_trpt_test_lib.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -1,19 +1,20 @@
 %%
 %% %CopyrightBegin%
-%% 
-%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
-%% 
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%% 
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%% 
+%%
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
 %% %CopyrightEnd%
 %%
 %%
@@ -23,7 +24,8 @@
 -export([exec/1, exec/2,
 	 instantiate/2,
 	 format_msg/1,
-	 server_host_port/1
+	 server_host_port/1,
+         return_value/1
 	]
        ).
 
@@ -47,7 +49,7 @@
 	  prints = [],
 	  return_value,
 
-          %% Packet retrival and decryption
+          %% Packet retrieval and decryption
           decrypted_data_buffer     = <<>>,
           encrypted_data_buffer     = <<>>,
           aead_data                 = <<>>,
@@ -73,7 +75,7 @@ exec(L, S) when is_list(L) -> lists:fold
 exec(Op, S0=#s{}) ->
     S1 = init_op_traces(Op, S0),
     try seqnum_trace(
-	  op(Op, S1))
+	  op(Op, S1), S1)
     of
 	S = #s{} ->
 	    case proplists:get_value(silent,S#s.opts) of
@@ -90,7 +92,8 @@ exec(Op, S0=#s{}) ->
 	    report_trace(throw, Term, S1),
 	    throw({Term,Op});
 
-	error:Error ->
+	error:Error:St ->
+            ct:log("Stacktrace=~n~p", [St]),
 	    report_trace(error, Error, S1),
 	    error({Error,Op});
 
@@ -331,19 +334,38 @@ send(S0, ssh_msg_kexinit) ->
     {Msg, _Bytes, _C0} = ssh_transport:key_exchange_init_msg(S0#s.ssh),
     send(S0, Msg);
 
+send(S0, ssh_msg_ignore) ->
+    Msg = #ssh_msg_ignore{data = "unexpected_ignore_message"},
+    send(S0, Msg);
+
+send(S0, ssh_msg_debug) ->
+    Msg = #ssh_msg_debug{
+             always_display = true,
+             message = "some debug message",
+             language = "en"},
+    send(S0, Msg);
+
+send(S0, ssh_msg_unimplemented) ->
+    Msg = #ssh_msg_unimplemented{sequence = 123},
+    send(S0, Msg);
+
+send(S0, ssh_msg_unknown) ->
+    Msg = binary:encode_hex(<<"0000000C060900000000000000000000">>),
+    send(S0, Msg);
+
 send(S0=#s{alg_neg={undefined,PeerMsg}}, Msg=#ssh_msg_kexinit{}) ->
     S1 = opt(print_messages, S0,
 	     fun(X) when X==true;X==detail -> {"Send~n~s~n",[format_msg(Msg)]} end),
     S2 = case PeerMsg of
 	     #ssh_msg_kexinit{} ->
-		 try ssh_transport:handle_kexinit_msg(PeerMsg, Msg, S1#s.ssh) of
+		 try ssh_transport:handle_kexinit_msg(PeerMsg, Msg, S1#s.ssh, init) of
 		     {ok,Cx} when ?role(S1) == server ->
 			 S1#s{alg = Cx#ssh.algorithms};
 		     {ok,_NextKexMsgBin,Cx} when ?role(S1) == client ->
 			 S1#s{alg = Cx#ssh.algorithms}
 		 catch
 		     Class:Exc ->
-			 save_prints({"Algoritm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s~n",
+			 save_prints({"Algorithm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s~n",
 				      [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(Msg)]},
 				     S1)
 		 end;
@@ -358,11 +380,11 @@ send(S0=#s{alg_neg={undefined,PeerMsg}},
 send(S0, ssh_msg_kexdh_init) when ?role(S0) == client ->
     {OwnMsg, PeerMsg} = S0#s.alg_neg,
     {ok, NextKexMsgBin, C} = 
-	try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S0#s.ssh)
+	try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S0#s.ssh, init)
 	catch
 	    Class:Exc ->
-		fail("Algoritm negotiation failed!",
-		     {"Algoritm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s",
+		fail("Algorithm negotiation failed!",
+		     {"Algorithm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s",
 		      [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(OwnMsg)]},
 		     S0)
 	end,
@@ -374,6 +396,26 @@ send(S0, ssh_msg_kexdh_init) when ?role(
 	    end),
     send_bytes(NextKexMsgBin, S#s{ssh = C});
 
+send(S0, ssh_msg_kexdh_init_dup) when ?role(S0) == client ->
+    {OwnMsg, PeerMsg} = S0#s.alg_neg,
+    {ok, NextKexMsgBin, C} =
+	try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S0#s.ssh, init)
+	catch
+	    Class:Exc ->
+		fail("Algorithm negotiation failed!",
+		     {"Algorithm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s",
+		      [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(OwnMsg)]},
+		     S0)
+	end,
+    S = opt(print_messages, S0,
+	    fun(X) when X==true;X==detail ->
+		    #ssh{keyex_key = {{_Private, Public}, {_G, _P}}} = C,
+		    Msg = #ssh_msg_kexdh_init{e = Public},
+		    {"Send (reconstructed)~n~s~n",[format_msg(Msg)]}
+	    end),
+    send_bytes(NextKexMsgBin, S#s{ssh = C}),
+    send_bytes(NextKexMsgBin, S#s{ssh = C});
+
 send(S0, ssh_msg_kexdh_reply) ->
     Bytes = proplists:get_value(ssh_msg_kexdh_reply, S0#s.reply),
     S = opt(print_messages, S0,
@@ -429,7 +471,7 @@ recv(S0 = #s{}) ->
 	    %% Must see hello before binary messages
 	    try_find_crlf(<<>>, S1);
 	true ->
-	    %% Has seen hello, therefore no more crlf-messages are alowed.
+	    %% Has seen hello, therefore no more crlf-messages are allowed.
 	    S = receive_binary_msg(S1),
 	    case PeerMsg = S#s.return_value of
 		#ssh_msg_kexinit{} ->
@@ -441,7 +483,7 @@ recv(S0 = #s{}) ->
 			    fail("2 kexint received!!", S);
 					
 			{OwnMsg, _} ->
-			    try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S#s.ssh) of
+			    try ssh_transport:handle_kexinit_msg(PeerMsg, OwnMsg, S#s.ssh, init) of
 				{ok,C} when ?role(S) == server ->
 				    S#s{alg_neg = {OwnMsg, PeerMsg},
 					alg = C#ssh.algorithms,
@@ -451,7 +493,7 @@ recv(S0 = #s{}) ->
 					alg = C#ssh.algorithms}
 			    catch
 				Class:Exc ->
-				    save_prints({"Algoritm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s~n",
+				    save_prints({"Algorithm negotiation failed at line ~p:~p~n~p:~s~nPeer: ~s~n Own: ~s~n",
 						 [?MODULE,?LINE,Class,format_msg(Exc),format_msg(PeerMsg),format_msg(OwnMsg)]},
 						S#s{alg_neg = {OwnMsg, PeerMsg}})
 			    end
@@ -523,7 +565,10 @@ receive_binary_msg(S0=#s{}) ->
            S0#s.ssh)
      of
          {packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} ->
-             S1 = S0#s{ssh = Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)},
+             S1 = S0#s{ssh = Ssh1#ssh{recv_sequence =
+                                          ssh_transport:next_seqnum(undefined,
+                                                                    Ssh1#ssh.recv_sequence,
+                                                                    false)},
                        decrypted_data_buffer = <<>>,
                        undecrypted_packet_length = undefined,
                        aead_data = <<>>,
@@ -650,7 +695,7 @@ ok({error,E}) -> erlang:error(E).
 
 %%%================================================================
 %%%
-%%% Formating of records
+%%% Formatting of records
 %%% 
 
 format_msg(M) -> format_msg(M, 0).
@@ -725,23 +770,23 @@ report_trace(Class, Term, S) ->
 	  fun(true) -> {"~s ~p",[Class,Term]} end)
      ).
 
-seqnum_trace(S) ->
+seqnum_trace(S, S0) ->
     opt(print_seqnums, S,
-	fun(true) when S#s.ssh#ssh.send_sequence =/= S#s.ssh#ssh.send_sequence,
-		       S#s.ssh#ssh.recv_sequence =/= S#s.ssh#ssh.recv_sequence ->
+	fun(true) when S0#s.ssh#ssh.send_sequence =/= S#s.ssh#ssh.send_sequence,
+		       S0#s.ssh#ssh.recv_sequence =/= S#s.ssh#ssh.recv_sequence ->
 		{"~p seq num: send ~p->~p,  recv ~p->~p~n",
 		 [?role(S),
-		  S#s.ssh#ssh.send_sequence, S#s.ssh#ssh.send_sequence,
-		  S#s.ssh#ssh.recv_sequence, S#s.ssh#ssh.recv_sequence
+		  S0#s.ssh#ssh.send_sequence, S#s.ssh#ssh.send_sequence,
+		  S0#s.ssh#ssh.recv_sequence, S#s.ssh#ssh.recv_sequence
 		 ]};
-	   (true) when S#s.ssh#ssh.send_sequence =/=  S#s.ssh#ssh.send_sequence ->
+	   (true) when S0#s.ssh#ssh.send_sequence =/=  S#s.ssh#ssh.send_sequence ->
 		{"~p seq num: send ~p->~p~n",
 		 [?role(S),
-		  S#s.ssh#ssh.send_sequence, S#s.ssh#ssh.send_sequence]};
-	   (true) when S#s.ssh#ssh.recv_sequence =/=  S#s.ssh#ssh.recv_sequence ->
+		  S0#s.ssh#ssh.send_sequence, S#s.ssh#ssh.send_sequence]};
+	   (true) when S0#s.ssh#ssh.recv_sequence =/=  S#s.ssh#ssh.recv_sequence ->
 		{"~p seq num: recv ~p->~p~n",
 		 [?role(S),
-		  S#s.ssh#ssh.recv_sequence, S#s.ssh#ssh.recv_sequence]}
+		  S0#s.ssh#ssh.recv_sequence, S#s.ssh#ssh.recv_sequence]}
 	end).
 
 print_traces(S) when S#s.prints == [] -> S;
@@ -770,3 +815,6 @@ opt(Flag, S, Fun) when is_function(Fun,1
 
 save_prints({Fmt,Args}, S) -> 
     S#s{prints = [{Fmt,Args}|S#s.prints]}.
+
+return_value(#s{return_value = ReturnValue}) ->
+    ReturnValue.
Index: otp-OTP-23.3.4.19/lib/ssh/vsn.mk
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/vsn.mk
+++ otp-OTP-23.3.4.19/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
 #-*-makefile-*-   ; force emacs to enter makefile-mode
 
-SSH_VSN = 4.11.1.6
+SSH_VSN = 5.1.4.13
 APP_VSN    = "ssh-$(SSH_VSN)"
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm_kexinit.erl
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm_kexinit.erl
@@ -0,0 +1,330 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Handles an ssh connection, e.i. both the
+%% setup SSH Transport Layer Protocol (RFC 4253), Authentication
+%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255)
+%% Details of the different protocols are
+%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl
+%% ----------------------------------------------------------------------
+
+-module(ssh_fsm_kexinit).
+
+-include("ssh.hrl").
+-include("ssh_transport.hrl").
+-include("ssh_auth.hrl").
+-include("ssh_connect.hrl").
+
+-include("ssh_fsm.hrl").
+
+%%====================================================================
+%%% Exports
+%%====================================================================
+
+%%% Behaviour callbacks
+-export([callback_mode/0, handle_event/4, terminate/3,
+	 format_status/2, code_change/4]).
+
+-behaviour(ssh_dbg).
+-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1,
+         ssh_dbg_on/1, ssh_dbg_off/1,
+         ssh_dbg_format/2]).
+
+%%====================================================================
+%% gen_statem callbacks
+%%====================================================================
+
+callback_mode() ->
+    [handle_event_function,
+     state_enter].
+
+%%--------------------------------------------------------------------
+
+
+handle_event(Type, Event = prepare_next_packet, StateName, D) ->
+    ssh_connection_handler:handle_event(Type, Event, StateName, D);
+handle_event(Type, Event = {send_disconnect, _, _, _, _}, StateName, D) ->
+    ssh_connection_handler:handle_event(Type, Event, StateName, D);
+
+%%% ######## {kexinit, client|server, init|renegotiate} ####
+handle_event(internal, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
+	     D = #data{key_exchange_init_msg = OwnKex}) ->
+    Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload),
+    Ssh = case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1, ReNeg) of
+	      {ok, NextKexMsg, Ssh2} when Role==client ->
+		  ssh_connection_handler:send_bytes(NextKexMsg, D),
+		  Ssh2;
+	      {ok, Ssh2} when Role==server ->
+		  Ssh2
+	  end,
+    {next_state, {key_exchange,Role,ReNeg}, D#data{ssh_params=Ssh}};
+
+%%% ######## {key_exchange, client|server, init|renegotiate} ####
+%%%---- diffie-hellman
+handle_event(internal, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(KexdhReply, D),
+    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
+    ssh_connection_handler:send_bytes(NewKeys, D),
+    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
+    ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
+
+handle_event(internal, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, NewKeys, Ssh1} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(NewKeys, D),
+    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
+    ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
+
+%%%---- diffie-hellman group exchange
+handle_event(internal, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(GexGroup, D),
+    Ssh = ssh_transport:parallell_gen_key(Ssh1),
+    {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
+
+handle_event(internal, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(GexGroup, D),
+    Ssh = ssh_transport:parallell_gen_key(Ssh1),
+    {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
+
+handle_event(internal, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(KexGexInit, D),
+    {next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}};
+
+%%%---- elliptic curve diffie-hellman
+handle_event(internal, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(KexEcdhReply, D),
+    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
+    ssh_connection_handler:send_bytes(NewKeys, D),
+    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
+    ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
+
+handle_event(internal, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(NewKeys, D),
+    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
+    ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
+
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {key_exchange,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                              [SendSeq, RecvSeq]));
+
+%%% ######## {key_exchange_dh_gex_init, server, init|renegotiate} ####
+handle_event(internal, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, KexGexReply, Ssh1} =  ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(KexGexReply, D),
+    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
+    ssh_connection_handler:send_bytes(NewKeys, D),
+    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
+    ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {key_exchange_dh_gex_init,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                              [SendSeq, RecvSeq]));
+
+%%% ######## {key_exchange_dh_gex_reply, client, init|renegotiate} ####
+handle_event(internal, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) ->
+    ok = check_kex_strict(Msg, D),
+    {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params),
+    ssh_connection_handler:send_bytes(NewKeys, D),
+    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
+    ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {key_exchange_dh_gex_reply,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                              [SendSeq, RecvSeq]));
+
+%%% ######## {new_keys, client|server} ####
+%% First key exchange round:
+handle_event(internal, #ssh_msg_newkeys{} = Msg, {new_keys,client,init}, D0) ->
+    {ok, Ssh1} = ssh_transport:handle_new_keys(Msg, D0#data.ssh_params),
+    %% {ok, ExtInfo, Ssh2} = ssh_transport:ext_info_message(Ssh1),
+    %% ssh_connection_handler:send_bytes(ExtInfo, D0),
+    {MsgReq, Ssh} = ssh_auth:service_request_msg(Ssh1),
+    D = ssh_connection_handler:send_msg(MsgReq, D0#data{ssh_params = Ssh}),
+    {next_state, {ext_info,client,init}, D};
+
+handle_event(internal, #ssh_msg_newkeys{} = Msg, {new_keys,server,init}, D) ->
+    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
+    %% {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
+    %% ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {ext_info,server,init}, D#data{ssh_params=Ssh}};
+
+%%% ######## handle KEX strict
+handle_event(internal, _Event, {new_keys,_Role,init},
+             #data{ssh_params = #ssh{algorithms = #alg{kex_strict_negotiated = true},
+                                     send_sequence = SendSeq,
+                                     recv_sequence = RecvSeq}}) ->
+    ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                io_lib:format("KEX strict violation (send_sequence = ~p recv_sequence = ~p)",
+                              [SendSeq, RecvSeq]));
+
+%% Subsequent key exchange rounds (renegotiation):
+handle_event(internal, #ssh_msg_newkeys{} = Msg, {new_keys,Role,renegotiate}, D) ->
+    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
+    %% {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
+    %% ssh_connection_handler:send_bytes(ExtInfo, D),
+    {next_state, {ext_info,Role,renegotiate}, D#data{ssh_params=Ssh}};
+
+
+%%% ######## {ext_info, client|server, init|renegotiate} ####
+
+handle_event(internal, #ssh_msg_ext_info{}=Msg, {ext_info,Role,init}, D0) ->
+    D = ssh_connection_handler:handle_ssh_msg_ext_info(Msg, D0),
+    {next_state, {service_request,Role}, D, {change_callback_module,ssh_connection_handler}};
+
+handle_event(internal, #ssh_msg_ext_info{}=Msg, {ext_info,Role,renegotiate}, D0) ->
+    D = ssh_connection_handler:handle_ssh_msg_ext_info(Msg, D0),
+    {next_state, {connected,Role}, D, {change_callback_module,ssh_connection_handler}};
+
+handle_event(internal, #ssh_msg_newkeys{}=Msg, {ext_info,_Role,renegotiate}, D) ->
+    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
+    {keep_state, D#data{ssh_params = Ssh}};
+
+handle_event(internal, Msg, {ext_info,Role,init}, D) when is_tuple(Msg) ->
+    %% If something else arrives, goto next state and handle the event in that one
+    {next_state, {service_request,Role}, D, [postpone, {change_callback_module,ssh_connection_handler}]};
+
+handle_event(internal, Msg, {ext_info,Role,renegotiate}, D) when is_tuple(Msg) ->
+    %% If something else arrives, goto next state and handle the event in that one
+    {next_state, {connected,Role}, D, [postpone, {change_callback_module,ssh_connection_handler}]};
+
+%%% ######## UNHANDLED EVENT!
+handle_event(Type, Event, StateName, D) ->
+    ssh_connection_handler:handle_event(Type, Event, StateName, D).
+
+%%--------------------------------------------------------------------
+format_status(A, B) ->
+    ssh_connection_handler:format_status(A, B).
+
+%%--------------------------------------------------------------------
+terminate(Reason, StateName, D) ->
+    ssh_connection_handler:terminate(Reason, StateName, D).
+
+%%--------------------------------------------------------------------
+code_change(_OldVsn, StateName, State, _Extra) ->
+    {ok, StateName, State}.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+%% "Invert" the Role
+peer_role(client) -> server;
+peer_role(server) -> client.
+
+check_kex_strict(Msg,
+                 #data{ssh_params =
+                           #ssh{algorithms =
+                                    #alg{
+                                       kex = Kex,
+                                       kex_strict_negotiated = KexStrictNegotiated},
+                                send_sequence = SendSeq,
+                                recv_sequence = RecvSeq}}) ->
+    case check_msg_group(Msg, get_alg_group(Kex), KexStrictNegotiated) of
+        ok ->
+            ok;
+        error ->
+            ?DISCONNECT(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+                        io_lib:format("KEX strict violation: send_sequence = ~p  recv_sequence = ~p",
+                                      [SendSeq, RecvSeq]))
+    end.
+
+get_alg_group(Kex) when Kex == 'diffie-hellman-group16-sha512';
+                        Kex == 'diffie-hellman-group18-sha512';
+                        Kex == 'diffie-hellman-group14-sha256';
+                        Kex == 'diffie-hellman-group14-sha1';
+                        Kex == 'diffie-hellman-group1-sha1' ->
+    dh_alg;
+get_alg_group(Kex) when Kex == 'diffie-hellman-group-exchange-sha256';
+                        Kex == 'diffie-hellman-group-exchange-sha1' ->
+    dh_gex_alg;
+get_alg_group(Kex) when Kex == 'curve25519-sha256';
+                        Kex == 'curve25519-sha256@libssh.org';
+                        Kex == 'curve448-sha512';
+                        Kex == 'ecdh-sha2-nistp521';
+                        Kex == 'ecdh-sha2-nistp384';
+                        Kex == 'ecdh-sha2-nistp256' ->
+    ecdh_alg.
+
+check_msg_group(_Msg, _AlgGroup, false) -> ok;
+check_msg_group(#ssh_msg_kexdh_init{},  dh_alg, true) -> ok;
+check_msg_group(#ssh_msg_kexdh_reply{}, dh_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_request_old{}, dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_request{},     dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_group{},       dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_init{},        dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_dh_gex_reply{},       dh_gex_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_ecdh_init{},  ecdh_alg, true) -> ok;
+check_msg_group(#ssh_msg_kex_ecdh_reply{}, ecdh_alg, true) -> ok;
+check_msg_group(_Msg, _AlgGroup, _) -> error.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+
+ssh_dbg_trace_points() -> [connection_events].
+
+ssh_dbg_flags(connection_events) -> [c].
+
+ssh_dbg_on(connection_events) -> dbg:tp(?MODULE,   handle_event, 4, x).
+
+ssh_dbg_off(connection_events) -> dbg:ctpg(?MODULE, handle_event, 4).
+
+ssh_dbg_format(connection_events, {call, {?MODULE,handle_event, [EventType, EventContent, State, _Data]}}) ->
+    ["Connection event\n",
+     io_lib:format("[~w] EventType: ~p~nEventContent: ~p~nState: ~p~n", [?MODULE, EventType, EventContent, State])
+    ];
+ssh_dbg_format(connection_events, {return_from, {?MODULE,handle_event,4}, Ret}) ->
+    ["Connection event result\n",
+     io_lib:format("[~w] ~p~n", [?MODULE, ssh_dbg:reduce_state(Ret, #data{})])
+    ].
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm_userauth_client.erl
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm_userauth_client.erl
@@ -0,0 +1,182 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2025. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Handles an ssh connection, e.i. both the
+%% setup SSH Transport Layer Protocol (RFC 4253), Authentication
+%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255)
+%% Details of the different protocols are
+%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl
+%% ----------------------------------------------------------------------
+
+-module(ssh_fsm_userauth_client).
+
+-include("ssh.hrl").
+-include("ssh_transport.hrl").
+-include("ssh_auth.hrl").
+-include("ssh_connect.hrl").
+
+-include("ssh_fsm.hrl").
+
+%%====================================================================
+%%% Exports
+%%====================================================================
+
+%%% Behaviour callbacks
+-export([callback_mode/0, handle_event/4, terminate/3,
+	 format_status/2, code_change/4]).
+
+%%====================================================================
+%% gen_statem callbacks
+%%====================================================================
+
+callback_mode() ->
+    [handle_event_function,
+     state_enter].
+
+%%--------------------------------------------------------------------
+
+%%% ######## {userauth, client} ####
+
+%%---- #ssh_msg_ext_info could follow after the key exchange, both the initial and the re-negotiation
+handle_event(internal, #ssh_msg_ext_info{}=Msg, {userauth,client}, D0) ->
+    %% FIXME: need new state to receive this msg!
+    D = ssh_connection_handler:handle_ssh_msg_ext_info(Msg, D0),
+    {keep_state, D};
+
+%%---- received userauth success from the server
+handle_event(internal, #ssh_msg_userauth_success{}, {userauth,client}, D0=#data{ssh_params = Ssh}) ->
+    ssh_auth:ssh_msg_userauth_result(success),
+    ssh_connection_handler:handshake(ssh_connected, D0),
+    D = D0#data{ssh_params=Ssh#ssh{authenticated = true}},
+    {next_state, {connected,client}, D, {change_callback_module,ssh_connection_handler}};
+
+
+%%---- userauth failure response to clientfrom the server
+handle_event(internal, #ssh_msg_userauth_failure{}, {userauth,client}=StateName,
+	     #data{ssh_params = #ssh{userauth_methods = []}} = D0) ->
+    {Shutdown, D} =
+        ?send_disconnect(?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 
+                         io_lib:format("User auth failed for: ~p",[D0#data.auth_user]),
+                         StateName, D0),
+    {stop, Shutdown, D};
+
+handle_event(internal, #ssh_msg_userauth_failure{authentications = Methods}, StateName={userauth,client},
+	     D0 = #data{ssh_params = Ssh0}) ->
+    %% The preferred authentication method failed, try next method
+    Ssh1 = case Ssh0#ssh.userauth_methods of
+	       none ->
+		   %% Server tells us which authentication methods that are allowed
+		   Ssh0#ssh{userauth_methods = string:tokens(Methods, ",")};
+	       _ ->
+		   %% We already know...
+		   Ssh0 
+	   end,
+    case ssh_auth:userauth_request_msg(Ssh1) of
+        {send_disconnect, Code, Ssh} ->
+            {Shutdown, D} =
+                ?send_disconnect(Code, 
+                                 io_lib:format("User auth failed for: ~p",[D0#data.auth_user]),
+                                 StateName, D0#data{ssh_params = Ssh}),
+	    {stop, Shutdown, D};
+	{"keyboard-interactive", {Msg, Ssh}} ->
+            D = ssh_connection_handler:send_msg(Msg, D0#data{ssh_params = Ssh}),
+	    {next_state, {userauth_keyboard_interactive,client}, D};
+	{_Method, {Msg, Ssh}} ->
+            D = ssh_connection_handler:send_msg(Msg, D0#data{ssh_params = Ssh}),
+	    {keep_state, D}
+    end;
+
+%%---- banner to client
+handle_event(internal, #ssh_msg_userauth_banner{message = Msg}, {S,client}, D)
+  when S == userauth; S == userauth_keyboard_interactive;
+       S == userauth_keyboard_interactive_extra;
+       S == userauth_keyboard_interactive_info_response ->
+    case D#data.ssh_params#ssh.userauth_quiet_mode of
+	false -> io:format("~s", [Msg]);
+	true -> ok
+    end,
+    keep_state_and_data;
+
+
+%%% ######## {userauth_keyboard_interactive, client}
+
+handle_event(internal, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client},
+	     #data{ssh_params = Ssh0} = D0) ->
+    case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of
+	{ok, {Reply, Ssh}} ->
+            D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}),
+	    {next_state, {userauth_keyboard_interactive_info_response,client}, D};
+	not_ok ->
+	    {next_state, {userauth,client}, D0, [postpone]}
+    end;
+
+handle_event(internal, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client},
+	     #data{ssh_params = Ssh0} = D0) ->
+    Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference,
+			       Method =/= "keyboard-interactive"],
+    D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}},
+    {next_state, {userauth,client}, D, [postpone]};
+
+handle_event(internal, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client},
+	     #data{ssh_params = Ssh0} = D0) ->
+    Opts = Ssh0#ssh.opts,
+    D = case ?GET_OPT(password, Opts) of
+	    undefined ->
+		D0;
+	    _ ->
+		D0#data{ssh_params =
+			    Ssh0#ssh{opts = ?PUT_OPT({password,not_ok}, Opts)}} % FIXME:intermodule dependency
+	end,
+    {next_state, {userauth,client}, D, [postpone]};
+
+handle_event(internal, #ssh_msg_ext_info{}=Msg, {userauth_keyboard_interactive_info_response, client}, D0) ->
+    %% FIXME: need new state to receive this msg!
+    D = ssh_connection_handler:handle_ssh_msg_ext_info(Msg, D0),
+    {keep_state, D};
+
+handle_event(internal, #ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) ->
+    {next_state, {userauth,client}, D, [postpone]};
+
+handle_event(internal, #ssh_msg_userauth_info_request{}, {userauth_keyboard_interactive_info_response, client}, D) ->
+    {next_state, {userauth_keyboard_interactive,client}, D, [postpone]};
+
+
+%%% ######## UNHANDLED EVENT!
+handle_event(Type, Event, StateName, D) ->
+    ssh_connection_handler:handle_event(Type, Event, StateName, D).
+
+%%--------------------------------------------------------------------
+format_status(A, B) ->
+    ssh_connection_handler:format_status(A, B).
+
+%%--------------------------------------------------------------------
+terminate(Reason, StateName, D) ->
+    ssh_connection_handler:terminate(Reason, StateName, D).
+
+%%--------------------------------------------------------------------
+code_change(_OldVsn, StateName, State, _Extra) ->
+    {ok, StateName, State}.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm_userauth_server.erl
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm_userauth_server.erl
@@ -0,0 +1,214 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2022. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Handles an ssh connection, e.i. both the
+%% setup SSH Transport Layer Protocol (RFC 4253), Authentication
+%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255)
+%% Details of the different protocols are
+%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl
+%% ----------------------------------------------------------------------
+
+-module(ssh_fsm_userauth_server).
+
+-include("ssh.hrl").
+-include("ssh_transport.hrl").
+-include("ssh_auth.hrl").
+-include("ssh_connect.hrl").
+
+-include("ssh_fsm.hrl").
+
+%%====================================================================
+%%% Exports
+%%====================================================================
+
+%%% Behaviour callbacks
+-export([callback_mode/0, handle_event/4, terminate/3,
+	 format_status/2, code_change/4]).
+
+%%====================================================================
+%% gen_statem callbacks
+%%====================================================================
+
+callback_mode() ->
+    [handle_event_function,
+     state_enter].
+
+%%--------------------------------------------------------------------
+
+%%% ######## {userauth, server} ####
+%%---- userauth request to server
+handle_event(internal, 
+	     Msg = #ssh_msg_userauth_request{service = ServiceName,
+                                             method = Method},
+	     StateName = {userauth,server},
+	     D0 = #data{ssh_params=Ssh0}) ->
+
+    case {ServiceName, Ssh0#ssh.service, Method} of
+	{"ssh-connection", "ssh-connection", "none"} ->
+	    %% Probably the very first userauth_request but we deny unauthorized login
+            %% However, we *may* accept unauthorized login if instructed so
+            case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of
+                {not_authorized, _, {Reply,Ssh}} ->
+                    D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}),
+                    {keep_state, D};
+                {authorized, User, {Reply, Ssh1}} ->
+                    D = connected_state(Reply, Ssh1, User, Method, D0),
+                    {next_state, {connected,server}, D,
+                     [set_max_initial_idle_timeout(D),
+                      {change_callback_module,ssh_connection_handler}
+                     ]
+                    }
+                     
+            end;
+	
+	{"ssh-connection", "ssh-connection", Method} ->
+	    %% Userauth request with a method like "password" or so
+	    case lists:member(Method, Ssh0#ssh.userauth_methods) of
+		true ->
+		    %% Yepp! we support this method
+		    case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of
+			{authorized, User, {Reply, Ssh1}} ->
+                            D = connected_state(Reply, Ssh1, User, Method, D0),
+                            {next_state, {connected,server}, D,
+                             [set_max_initial_idle_timeout(D),
+                              {change_callback_module,ssh_connection_handler}
+                             ]};
+			{not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" ->
+			    retry_fun(User, Reason, D0),
+                            D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}),
+			    {next_state, {userauth_keyboard_interactive,server}, D};
+			{not_authorized, {User, Reason}, {Reply, Ssh}} ->
+			    retry_fun(User, Reason, D0),
+                            D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}),
+			    {keep_state, D}
+		    end;
+		false ->
+		    %% No we do not support this method (=/= none)
+		    %% At least one non-erlang client does like this. Retry as the next event
+		    {keep_state_and_data,
+		     [{next_event, internal, Msg#ssh_msg_userauth_request{method="none"}}]
+		    }
+	    end;
+
+	%% {"ssh-connection", Expected, Method} when Expected =/= ServiceName -> Do what?
+	%% {ServiceName,      Expected, Method} when Expected =/= ServiceName -> Do what?
+
+	{ServiceName, _, _} when ServiceName =/= "ssh-connection" ->
+            {Shutdown, D} =  
+                ?send_disconnect(?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+                                 io_lib:format("Unknown service: ~p",[ServiceName]),
+                                 StateName, D0),
+            {stop, Shutdown, D}
+    end;
+
+handle_event(internal, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D0) ->
+    case ssh_auth:handle_userauth_info_response(Msg, D0#data.ssh_params) of
+	{authorized, User, {Reply, Ssh1}} ->
+            D = connected_state(Reply, Ssh1, User, "keyboard-interactive", D0),
+            {next_state, {connected,server}, D,
+             [set_max_initial_idle_timeout(D),
+              {change_callback_module,ssh_connection_handler}
+             ]};
+	{not_authorized, {User, Reason}, {Reply, Ssh}} ->
+	    retry_fun(User, Reason, D0),
+            D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}),
+	    {next_state, {userauth,server}, D};
+
+	{authorized_but_one_more, _User,  {Reply, Ssh}} ->
+            D = ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh}),
+	    {next_state, {userauth_keyboard_interactive_extra,server}, D}
+    end;
+
+handle_event(internal, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive_extra, server}, D0) ->
+    {authorized, User, {Reply, Ssh1}} =
+        ssh_auth:handle_userauth_info_response({extra,Msg}, D0#data.ssh_params),
+    D = connected_state(Reply, Ssh1, User, "keyboard-interactive", D0),
+    {next_state, {connected,server}, D,
+     [set_max_initial_idle_timeout(D),
+      {change_callback_module,ssh_connection_handler}
+     ]
+    };
+
+
+%%% ######## UNHANDLED EVENT!
+handle_event(Type, Event, StateName, D) ->
+    ssh_connection_handler:handle_event(Type, Event, StateName, D).
+
+%%--------------------------------------------------------------------
+format_status(A, B) ->
+    ssh_connection_handler:format_status(A, B).
+
+%%--------------------------------------------------------------------
+terminate(Reason, StateName, D) ->
+    ssh_connection_handler:terminate(Reason, StateName, D).
+
+%%--------------------------------------------------------------------
+code_change(_OldVsn, StateName, State, _Extra) ->
+    {ok, StateName, State}.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+connected_state(Reply, Ssh1, User, Method, D0) ->
+    D1 = #data{ssh_params=Ssh} =
+        ssh_connection_handler:send_msg(Reply, D0#data{ssh_params = Ssh1}),
+    ssh_connection_handler:handshake(ssh_connected, D1),
+    connected_fun(User, Method, D1),
+    D1#data{auth_user=User,
+            %% Note: authenticated=true MUST NOT be sent
+            %% before send_msg!
+            ssh_params = Ssh#ssh{authenticated = true}}.
+
+
+set_max_initial_idle_timeout(#data{ssh_params = #ssh{opts=Opts}}) ->
+    {{timeout,max_initial_idle_time}, ?GET_OPT(max_initial_idle_time,Opts), none}.
+
+connected_fun(User, Method, #data{ssh_params = #ssh{peer = {_,Peer}}} = D) ->
+    ?CALL_FUN(connectfun,D)(User, Peer, Method).
+
+
+retry_fun(_, undefined, _) ->
+    ok;
+retry_fun(User, Reason, #data{ssh_params = #ssh{opts = Opts,
+						peer = {_,Peer}
+					       }}) ->
+    {Tag,Info} =
+	case Reason of
+	    {error, Error} ->
+		{failfun, Error};
+	    _ ->
+		{infofun, Reason}
+	end,
+    Fun = ?GET_OPT(Tag, Opts),
+    try erlang:fun_info(Fun, arity)
+    of
+	{arity, 2} -> %% Backwards compatible
+	    catch Fun(User, Info);
+	{arity, 3} ->
+	    catch Fun(User, Peer, Info);
+	_ ->
+	    ok
+    catch
+	_:_ ->
+	    ok
+    end.
+
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_lib.erl
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_lib.erl
@@ -0,0 +1,101 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2025. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Common library routines in the SSH application
+%% 
+
+-module(ssh_lib).
+
+-export([
+         format_address_port/2, format_address_port/1,
+         format_address/1,
+         format_time_ms/1,
+         comp/2,
+         trim_reason/1,
+         max_log_len/1
+        ]).
+
+-include("ssh.hrl").
+
+%%%----------------------------------------------------------------
+format_address_port({IP,Port}) when is_integer(Port) ->
+    format_address_port(IP, Port);
+format_address_port(X) ->
+    io_lib:format("~p", [X]).
+
+%%%----------------------------------------------------------------
+format_address_port(Address, Port) ->
+    try lists:concat([format_address(Address), ":", Port])
+    catch
+        _:_ -> io_lib:format("~p:~p",[Address,Port])
+    end.
+
+%%%----------------------------------------------------------------
+format_address(#address{address=A, port=P}) ->
+    format_address_port(A,P);
+format_address(A) ->
+    try inet:ntoa(A)
+    catch
+        _:_ when is_list(A) -> A;
+        _:_ -> io_lib:format('~p',[A])
+    end.
+
+%%%----------------------------------------------------------------
+format_time_ms(T) when is_integer(T) ->
+    if
+        T < 60000 -> io_lib:format("~.3f sec", [T/1000]);
+        true -> io_lib:format("~p min ~s", [T div 60000, format_time_ms(T rem 60000)])
+    end.
+
+            
+%%%----------------------------------------------------------------
+
+comp(X1, X2) ->
+    comp(X1, X2, true).
+
+%%% yes - very far from best implementation
+comp(<<B1,R1/binary>>, <<B2,R2/binary>>, Truth) ->
+    comp(R1, R2, Truth and (B1 == B2));
+comp(<<_,R1/binary>>, <<>>, Truth) ->
+    comp(R1, <<>>, Truth and false);
+comp(<<>>, <<>>, Truth) ->
+    Truth;
+
+comp([H1|T1], [H2|T2], Truth) ->
+    comp(T1, T2, Truth and (H1 == H2));
+comp([_|T1], [], Truth) ->
+    comp(T1, [], Truth and false);
+comp([], [], Truth) ->
+    Truth;
+
+comp(_, _, _) ->
+    false.
+%% We don't want to process badmatch details, potentially containing
+%% malicious data of unknown size
+trim_reason({badmatch, V}) when is_binary(V) ->
+    badmatch;
+trim_reason(E) ->
+    E.
+
+max_log_len(#ssh{opts = Opts}) ->
+    ?GET_OPT(max_log_item_len, Opts);
+max_log_len(Opts) when is_map(Opts) ->
+    ?GET_OPT(max_log_item_len, Opts).
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm.hrl
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_fsm.hrl
@@ -0,0 +1,57 @@
+%%====================================================================
+%% Types
+%%====================================================================
+-type connection_ref() :: ssh:connection_ref().
+-type channel_id()     :: ssh:channel_id().
+
+%%====================================================================
+%% Internal process state
+%%====================================================================
+-record(data, {
+	  starter                               :: pid()
+						 | undefined,
+	  auth_user                             :: string()
+						 | undefined,
+	  connection_state                      :: #connection{}
+						 | undefined,
+	  latest_channel_id         = 0         :: non_neg_integer()
+                                                 | undefined,
+	  transport_protocol                    :: atom()
+                                                 | undefined,	% ex: tcp
+	  transport_cb                          :: atom()
+                                                 | undefined,	% ex: gen_tcp
+	  transport_close_tag                   :: atom()
+                                                 | undefined,	% ex: tcp_closed
+	  ssh_params                            :: #ssh{}
+                                                 | undefined,
+	  socket                                :: gen_tcp:socket()
+                                                 | undefined,
+	  decrypted_data_buffer     = <<>>      :: binary()
+                                                 | undefined,
+	  encrypted_data_buffer     = <<>>      :: binary()
+                                                 | undefined,
+	  aead_data                 = <<>>      :: binary()
+                                                 | undefined,
+	  undecrypted_packet_length             :: undefined | non_neg_integer(),
+	  key_exchange_init_msg                 :: #ssh_msg_kexinit{}
+						 | undefined,
+	  last_size_rekey           = 0         :: non_neg_integer(),
+	  event_queue               = []        :: list(),
+	  inet_initial_buffer_size              :: pos_integer()
+						 | undefined
+	 }).
+
+
+%%====================================================================
+%% Macros
+%%====================================================================
+-define(send_disconnect(Code, DetailedText, StateName, State),
+        ssh_connection_handler:send_disconnect(Code, DetailedText, ?MODULE, ?LINE, StateName, State)).
+
+-define(send_disconnect(Code, Reason, DetailedText, StateName, State),
+        ssh_connection_handler:send_disconnect(Code, Reason, DetailedText, ?MODULE, ?LINE, StateName, State)).
+
+
+-define(CALL_FUN(Key,D), catch (?GET_OPT(Key, (D#data.ssh_params)#ssh.opts)) ).
+
+-define(role(StateName), element(2,StateName)).
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_acceptor_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_acceptor_sup.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_acceptor_sup.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -29,64 +29,43 @@
 
 -include("ssh.hrl").
 
--export([start_link/4, start_child/5, stop_child/4]).
+-export([start_link/3,
+         restart_child/2
+        ]).
 
 %% Supervisor callback
 -export([init/1]).
 
--define(DEFAULT_TIMEOUT, 50000).
-
 %%%=========================================================================
 %%%  API
 %%%=========================================================================
-start_link(Address, Port, Profile, Options) ->
-    supervisor:start_link(?MODULE, [Address, Port, Profile, Options]).
-
-start_child(AccSup, Address, Port, Profile, Options) ->
-    Spec = child_spec(Address, Port, Profile, Options),
-    case supervisor:start_child(AccSup, Spec) of
-	{error, already_present} ->
-            %% Is this ever called?
-	    stop_child(AccSup, Address, Port, Profile),
-	    supervisor:start_child(AccSup, Spec);
-	Reply ->
-            %% Reply = {ok,SystemSupPid} when the user calls ssh:daemon
-            %% after having called ssh:stop_listening
-	    Reply
+start_link(SystemSup, Address, Options) ->
+    case supervisor:start_link(?MODULE, [SystemSup, Address, Options]) of
+        {error, {shutdown, {failed_to_start_child, _, Error}}} ->
+            {error,Error};
+        Other ->
+            Other
     end.
 
-stop_child(AccSup, Address, Port, Profile) ->
-    Name = id(Address, Port, Profile),
-    case supervisor:terminate_child(AccSup, Name) of
-        ok ->
-            supervisor:delete_child(AccSup, Name);
-        Error ->
-            Error
-    end.
+restart_child(AccSup, Address) ->
+    supervisor:restart_child(AccSup, {?MODULE,Address}).
 
 %%%=========================================================================
 %%%  Supervisor callback
 %%%=========================================================================
-init([Address, Port, Profile, Options]) ->
-    %% Initial start of ssh_acceptor_sup for this port or new start after
-    %% ssh:stop_daemon
+init([SystemSup, Address, Options]) ->
+    %% Initial start of ssh_acceptor_sup for this port
     SupFlags = #{strategy  => one_for_one, 
                  intensity =>   10,
                  period    => 3600
                 },
-    ChildSpecs = [child_spec(Address, Port, Profile, Options)],
+    ChildSpecs = [#{id       => {?MODULE,Address},
+                    start    => {ssh_acceptor, start_link, [SystemSup, Address, Options]},
+                    restart  => transient % because a crashed listener could be replaced by a new one
+                   }
+                 ],
     {ok, {SupFlags,ChildSpecs}}.
 
 %%%=========================================================================
 %%%  Internal functions
 %%%=========================================================================
-child_spec(Address, Port, Profile, Options) ->
-    Timeout = ?GET_INTERNAL_OPT(timeout, Options, ?DEFAULT_TIMEOUT),
-    #{id       => id(Address, Port, Profile),
-      start    => {ssh_acceptor, start_link, [Port, Address, Options, Timeout]},
-      restart  => transient % because a crashed listener could be replaced by a new one
-     }.
-
-id(Address, Port, Profile) ->
-    {ssh_acceptor_sup, Address, Port, Profile}.
-
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_auth.hrl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_auth.hrl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_auth.hrl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2022. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@
 -record(ssh_msg_userauth_passwd_changereq,
 	{
 	  prompt,     %% string
-	  languge     %% string
+	  language     %% string
 	 }).
 
 -record(ssh_msg_userauth_pk_ok,
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_controller.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_controller.erl
+++ /dev/null
@@ -1,125 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: Handles an ssh connection, e.i. both the
-%% setup SSH Transport Layer Protocol (RFC 4253), Authentication
-%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255)
-%% Details of the different protocols are
-%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl
-%% ----------------------------------------------------------------------
-
--module(ssh_controller).
-
--behaviour(gen_server).
-
-
-%%====================================================================
-%%% Exports
-%%====================================================================
-
-%%% API
--export([start_system_subsystem/7,
-         stop_system/2
-        ]).
-
-%%% Start and stop
--export([start_link/2
-	]).
-
--export([init/1,
-         handle_call/3,
-         handle_cast/2
-        ]).
-
-%%====================================================================
-%% Start / stop
-%%====================================================================
-
-start_link(Role, RegName) ->
-    gen_server:start_link({local,RegName}, ?MODULE, [Role], []).
-
-%%====================================================================
-%% Internal application API
-%%====================================================================
-
--define(TIMEOUT, 30000).
-
-start_system_subsystem(Controller, Sup, Host, Port, Profile, Options, ChildSpec) ->
-    gen_server:call(Controller, {start_system_subsystem, Sup, Host, Port, Profile, Options, ChildSpec}, ?TIMEOUT).
-
-stop_system(Controller, SysSup) ->
-    gen_server:call(Controller, {stop_system,SysSup}, ?TIMEOUT).
-
-%%====================================================================
-%% Internal process state
-%%====================================================================
--record(data, {
-          role
-	 }).
-
-%%====================================================================
-%% Intitialisation
-%%====================================================================
-init([Role]=_Args) ->
-    {ok, #data{role=Role}}.
-        
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-handle_call({start_system_subsystem, Sup, Address, Port, Profile, Options, ChildSpec}, _From, D) ->
-    try
-        {ok,SystemSup0} =
-            case ssh_system_sup:system_supervisor(Address, Port, Profile) of
-                undefined ->
-                    supervisor:start_child(Sup, ChildSpec);
-                Pid ->
-                    {ok,Pid}
-            end,
-        {SystemSup0, ssh_system_sup:start_subsystem(SystemSup0, D#data.role, Address, Port, Profile, Options)}
-    of
-        {SystemSup, {ok,SubSysSup}} ->
-             {reply, {ok,{SystemSup,SubSysSup}}, D}
-    catch
-        C:E:S ->
-            {reply, {error,{failed,C,E,S}}, D}
-    end;
-
-
-handle_call({stop_system,SysSup}, _From, D) ->
-    try
-        case supervisor:which_children(SysSup) of
-            [] ->
-                ssh_system_sup:stop_system(D#data.role, SysSup);
-            _X ->
-                ok
-        end
-    catch
-        _:_ ->
-            %% Already stopped (?)
-            skip
-    end,
-    {reply, ok, D}.
-
-
-
-
-handle_cast(_Request, D) ->
-    {noreply, D}.
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_subsystem_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_subsystem_sup.erl
+++ /dev/null
@@ -1,121 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: The ssh subsystem supervisor 
-%%----------------------------------------------------------------------
-
--module(ssh_subsystem_sup).
-
--behaviour(supervisor).
-
--include("ssh.hrl").
-
--export([start_link/5,
-	 connection_supervisor/1,
-	 channel_supervisor/1,
-         tcpip_fwd_supervisor/1,
-         start_channel/8
-	]).
-
-%% Supervisor callback
--export([init/1]).
-
-%%%=========================================================================
-%%%  API
-%%%=========================================================================
-start_link(Role, Address, Port, Profile, Options) ->
-    supervisor:start_link(?MODULE, [Role, Address, Port, Profile, Options]).
-
-connection_supervisor(SupPid) ->
-    Children = supervisor:which_children(SupPid),
-    ssh_connection_sup(Children).
-
-channel_supervisor(SupPid) when is_pid(SupPid) ->    
-    Children = supervisor:which_children(SupPid),
-    ssh_channel_sup(Children).
-
-tcpip_fwd_supervisor(SupPid) when is_pid(SupPid) ->    
-    Children = supervisor:which_children(SupPid),
-    tcpip_fwd_sup(Children).
-
-start_channel(Role, SupPid, ConnRef, Callback, Id, Args, Exec, Opts) ->
-    ChannelSup = channel_supervisor(SupPid),
-    ssh_channel_sup:start_child(Role, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts).
-
-%%%=========================================================================
-%%%  Supervisor callback
-%%%=========================================================================
-init([Role, Address, Port, Profile, Options]) ->
-    SupFlags = #{strategy  => one_for_all,
-                 intensity =>    0,
-                 period    => 3600
-                },
-    ChildSpecs = child_specs(Role, Address, Port, Profile, Options),
-    {ok, {SupFlags,ChildSpecs}}.
-
-%%%=========================================================================
-%%%  Internal functions
-%%%=========================================================================
-child_specs(Role, Address, Port, Profile, Options) ->
-    [ssh_channel_child_spec(Role, Address, Port, Profile, Options), 
-     ssh_connection_child_spec(Role, Address, Port, Profile, Options),
-     ssh_tcpip_forward_acceptor_child_spec()].
-  
-ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
-    #{id       => id(Role, ssh_connection_sup, Address, Port),
-      start    => {ssh_connection_sup, start_link, [Options]},
-      restart  => temporary,
-      type     => supervisor
-     }.
-
-ssh_channel_child_spec(Role, Address, Port, _Profile, Options) ->
-    #{id       => id(Role, ssh_channel_sup, Address, Port),
-      start    => {ssh_channel_sup, start_link, [Options]},
-      restart  => temporary,
-      type     => supervisor
-     }.
-
-ssh_tcpip_forward_acceptor_child_spec() ->
-    #{id       => make_ref(),
-      start    => {ssh_tcpip_forward_acceptor_sup, start_link, []},
-      restart  => temporary,
-      type     => supervisor
-     }.
-
-
-id(Role, Sup, Address, Port) ->
-    {Role, Sup, Address, Port}.
-
-ssh_connection_sup([{_, Child, _, [ssh_connection_sup]} | _]) ->
-    Child;
-ssh_connection_sup([_ | Rest]) ->
-    ssh_connection_sup(Rest).
-
-ssh_channel_sup([{_, Child, _, [ssh_channel_sup]} | _]) ->
-    Child;
-ssh_channel_sup([_ | Rest]) ->
-    ssh_channel_sup(Rest).
-
-tcpip_fwd_sup([{_, Child, _, [ssh_tcpip_forward_acceptor_sup]} | _]) ->
-    Child;
-tcpip_fwd_sup([_ | Rest]) ->
-    tcpip_fwd_sup(Rest).
-
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_sup.erl
+++ /dev/null
@@ -1,118 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%%----------------------------------------------------------------------
-%% Purpose: The top supervisor for the ssh application.
-%%----------------------------------------------------------------------
--module(ssh_sup).
-
--behaviour(supervisor).
-
--export([init/1]).
-
-%%%=========================================================================
-%%%  Supervisor callback
-%%%=========================================================================
-init(_) ->
-    add_logger_filter(),
-    SupFlags = #{strategy  => one_for_one, 
-                 intensity =>   10,
-                 period    => 3600
-                },
-    ChildSpecs = [#{id       => sshd_sup,
-                    start    => {sshd_sup, start_link, []},
-                    type     => supervisor
-                   },
-                  #{id       => sshc_sup,
-                    start    => {sshc_sup, start_link, []},
-                    type     => supervisor
-                   }
-                 ],
-    {ok, {SupFlags,ChildSpecs}}.
-
-
-%%%================================================================
-add_logger_filter() ->
-    DefAct = application:get_env(ssh, default_filter, rm),
-    DefF = start_link,
-    ModulesActions =
-        lists:map(fun(M) when is_atom(M) ->
-                          {M,{DefF,DefAct}};
-
-                     ({M,Act}) when is_atom(M),
-                                    (Act == rm orelse
-                                     Act == filter) ->
-                          {M,{DefF,Act}};
-
-                     ({M,F}) when is_atom(M), is_atom(F) ->
-                          {M,{F,DefAct}};
-
-                     ({M,F,Act}) when is_atom(M), is_atom(F),
-                                      (Act == rm orelse
-                                       Act == filter) ->
-                          {M,{F,Act}}
-                  end, application:get_env(ssh, filter_modules, [])),
-    logger:add_primary_filter(ssh_filter, {fun ssh_filter/2, ModulesActions}).
-
-
-ssh_filter(Ev = #{msg := {report, R=#{report := Rep}}},
-           ModulesActions = [_|_]) when is_list(Rep) ->
-    %% io:format("Ev = ~p~n", [Ev]),
-    try
-        Ev#{msg := {report, R#{report := remove_sensitive(Rep, ModulesActions)}}}
-    catch
-        throw:{ssh_filter_return,Ret} -> 
-            %% io:format("ssh_filter returns ~p~n", [Ret]),
-            Ret;
-        _C:_E ->
-            %% io:format("*** ~p ~p~n", [_C,_E]),
-            stop
-    end;
-ssh_filter(OtherEv, _) ->
-    %% io:format("OtherEv = ~p~n", [OtherEv]),
-    OtherEv.
-
-
-remove_sensitive(L, ModActs) when is_list(L) -> rs(L, ModActs);
-remove_sensitive(_, _) -> throw({ssh_filter_return,ignore}).
-
-
-rs([{K,V0}|T], ModActs) when is_list(V0) ->
-    case proplists:get_value(mfargs, V0) of
-        {M,F,A} ->
-            MFA1 = filter(proplists:get_value(M,ModActs), {M,F,A}),
-            V = lists:keyreplace(mfargs, 1, V0, {mfargs,MFA1}),
-            [{K,V} | T];
-        _ ->
-            [{K,V0} | rs(T,ModActs)]
-    end;
-
-rs([H|T], ModActs) ->
-    [H | rs(T,ModActs)];
-
-rs(Other, _) ->
-    Other.
-
-
-filter({F,Act}, {M,F,A}) -> {M, F, ssh_options:no_sensitive(Act,A)};
-filter(stop, _) -> throw({ssh_filter_return,stop});
-filter(_, _) -> throw({ssh_filter_return,ignore}).
-
Index: otp-OTP-23.3.4.19/lib/ssh/src/sshc_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/sshc_sup.erl
+++ /dev/null
@@ -1,109 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%%----------------------------------------------------------------------
-%% Purpose: The ssh client subsystem supervisor 
-%%----------------------------------------------------------------------
-
--module(sshc_sup).
-
--behaviour(supervisor).
-
--export([start_link/0,
-         start_child/4,
-         start_system_subsystem/4,
-         stop_child/1,
-         stop_system/1
-        ]).
-
-%% Supervisor callback
--export([init/1]).
-
--include("ssh.hrl").
-
--define(SSHC_SUP, ?MODULE).
-
-%%%=========================================================================
-%%%  API
-%%%=========================================================================
-start_link() ->
-    supervisor:start_link({local,?MODULE}, ?MODULE, []).
-
-start_child(Address, Port, Profile, Options) ->
-    case ssh_system_sup:system_supervisor(Address, Port, Profile) of
-     undefined ->
-            %% Here we a new connction on a new Host/Port/Profile
-            Spec = child_spec(Address, Port, Profile, Options),
-            supervisor:start_child(?MODULE, Spec);
-	Pid ->
-            {ok,Pid}
-    end.
-
-
-start_system_subsystem(Host, Port, Profile, Options0) ->
-    Options = ?DELETE_OPT(password, Options0),
-    ssh_controller:start_system_subsystem(client_controller, ?MODULE, Host, Port, Profile, Options,
-                                          child_spec(Host, Port, Profile, Options)
-                                         ).
-
-stop_child(ChildId) when is_tuple(ChildId) ->
-    supervisor:terminate_child(?SSHC_SUP, ChildId);
-stop_child(ChildPid) when is_pid(ChildPid)->
-    stop_child(system_name(ChildPid)).
-
-stop_system(SysSup) ->
-    ssh_controller:stop_system(client_controller, SysSup).
-
-
-%%%=========================================================================
-%%%  Supervisor callback
-%%%=========================================================================
-init(_) ->
-    SupFlags = #{strategy  => one_for_one, 
-                 intensity =>    0,
-                 period    => 3600
-                },
-    ChildSpecs = [#{id       => client_controller,
-                    start    => {ssh_controller, start_link, [client, client_controller]},
-                    restart  => permanent,
-                    type     => worker
-                   }],
-    {ok, {SupFlags,ChildSpecs}}.
-
-%%%=========================================================================
-%%%  Internal functions
-%%%=========================================================================
-child_spec(Address, Port, Profile, Options) ->
-    #{id       => id(Address, Port, Profile),
-      start    => {ssh_system_sup, start_link, [client, Address, Port, Profile, Options]},
-      restart  => temporary,
-      type     => supervisor
-     }.
-
-id(Address, Port, Profile) ->
-    {client, ssh_system_sup, Address, Port, Profile}.
-
-system_name(SysSup) ->
-    case lists:keyfind(SysSup, 2, supervisor:which_children(?SSHC_SUP)) of
-        {Name, SysSup, _, _} -> Name;
-        false -> undefind
-    end.
-
Index: otp-OTP-23.3.4.19/lib/ssh/src/sshd_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/sshd_sup.erl
+++ /dev/null
@@ -1,105 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%%     http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: The top supervisor for ssh servers hangs under
-%%          ssh_sup.
-%%----------------------------------------------------------------------
-
--module(sshd_sup).
-
--behaviour(supervisor).
-
--include("ssh.hrl").
-
--export([start_link/0,
-         start_child/4,
-         stop_child/1,
-	 stop_child/3
-]).
-
-%% Supervisor callback
--export([init/1]).
-
--define(SSHD_SUP, ?MODULE).
-
-%%%=========================================================================
-%%%  API
-%%%=========================================================================
-start_link() ->
-    %% No children are start now. We wait until the user calls ssh:daemon
-    %% and uses start_child/4 to create the children
-    supervisor:start_link({local,?SSHD_SUP}, ?MODULE, []).
-
-start_child(Address, Port, Profile, Options) ->
-    case ssh_system_sup:system_supervisor(Address, Port, Profile) of
-       undefined ->
-            %% Here we start listening on a new Host/Port/Profile
-	    Spec = child_spec(Address, Port, Profile, Options),
-            supervisor:start_child(?SSHD_SUP, Spec);
-	Pid ->
-            %% Here we resume listening on a new Host/Port/Profile after
-            %% haveing stopped listening to he same with ssh:stop_listen(Pid)
-	    AccPid = ssh_system_sup:acceptor_supervisor(Pid),
-            ssh_acceptor_sup:start_child(AccPid, Address, Port, Profile, Options),
-            {ok,Pid}
-    end.
-
-stop_child(ChildId) when is_tuple(ChildId) ->
-    supervisor:terminate_child(?SSHD_SUP, ChildId);
-stop_child(ChildPid) when is_pid(ChildPid)->
-    stop_child(system_name(ChildPid)).
-
-
-stop_child(Address, Port, Profile) ->
-    Id = id(Address, Port, Profile),
-    stop_child(Id).
-
-%%%=========================================================================
-%%%  Supervisor callback
-%%%=========================================================================
-init(_) ->
-    SupFlags = #{strategy  => one_for_one,
-                 intensity =>   10,
-                 period    => 3600
-                },
-    ChildSpecs = [
-                 ],
-    {ok, {SupFlags,ChildSpecs}}.
-
-%%%=========================================================================
-%%%  Internal functions
-%%%=========================================================================
-child_spec(Address, Port, Profile, Options) ->
-    #{id       => id(Address, Port, Profile),
-      start    => {ssh_system_sup, start_link, [server, Address, Port, Profile, Options]},
-      restart  => temporary, 
-      type     => supervisor
-     }.
-
-id(Address, Port, Profile) ->
-    {server, ssh_system_sup, Address, Port, Profile}.
-
-system_name(SysSup) ->
-    case lists:keyfind(SysSup, 2, supervisor:which_children(?SSHD_SUP)) of
-        {Name, SysSup, _, _} -> Name;
-        false -> undefind
-    end.
-
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_agent.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_agent.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_agent.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2019. All Rights Reserved.
+%% Copyright Ericsson AB 2019-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -173,7 +173,7 @@ send(Request, Opts) ->
 %% Message packing
 
 pack(Data) ->
-    <<(size(Data)):32/unsigned-big-integer, Data/binary>>.
+    <<(byte_size(Data)):32/unsigned-big-integer, Data/binary>>.
 
 %% SSH Agent message encoding
 
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_auth.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_auth.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_auth.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2022. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -149,7 +149,7 @@ get_public_key(SigAlg, #ssh{opts = Opts}
             try
                 %% Check the key - the KeyCb may be a buggy plugin
                 true = ssh_transport:valid_key_sha_alg(private, PrivKey, KeyAlg),
-                Key = ssh_transport:extract_public_key(PrivKey),
+                Key = ssh_file:extract_public_key(PrivKey),
                 ssh_message:ssh2_pubkey_encode(Key)
             of
                 PubKeyBlob -> {ok, {PrivKey, PubKeyBlob}}
@@ -172,26 +172,28 @@ publickey_msg([SigAlg, #ssh{user = User,
             SigAlgStr = atom_to_list(SigAlg),
             SigData = build_sig_data(SessionId, User, Service, PubKeyBlob, SigAlgStr),
 
-            Sig = case Key of
-                {ssh2_pubkey, PubKeyBlob} ->
-                  ssh_transport:call_KeyCb(sign, [PubKeyBlob, SigData], Opts);
-
-                {PrivKey, PubKeyBlob} ->
-                  Hash = ssh_transport:sha(SigAlg),
-                  ssh_transport:sign(SigData, Hash, PrivKey)
-              end,
-
-            SigBlob = list_to_binary([?string(SigAlgStr),
-                                      ?binary(Sig)]),
-
-            {#ssh_msg_userauth_request{user = User,
-                                       service = Service,
-                                       method = "publickey",
-                                       data = [?TRUE,
-                                               ?string(SigAlgStr),
-                                               ?binary(PubKeyBlob),
-                                               ?binary(SigBlob)]},
-             Ssh};
+            SigRes = case Key of
+                         {ssh2_pubkey, PubKeyBlob} ->
+                             {ok, ssh_transport:call_KeyCb(sign, [PubKeyBlob, SigData], Opts)};
+                         {PrivKey, PubKeyBlob} ->
+                             ssh_transport:sign(SigData, SigAlg, PrivKey, Ssh)
+                     end,
+            case SigRes of
+                {ok,Sig} ->
+                    SigBlob = list_to_binary([?string(SigAlgStr),
+                                              ?binary(Sig)]),
+
+                    {#ssh_msg_userauth_request{user = User,
+                                               service = Service,
+                                               method = "publickey",
+                                               data = [?TRUE,
+                                                       ?string(SigAlgStr),
+                                                       ?binary(PubKeyBlob),
+                                                       ?binary(SigBlob)]},
+                     Ssh};
+                {error,_} ->
+                    {not_ok, Ssh}
+            end;
 
         _ ->
             {not_ok, Ssh}
@@ -270,11 +272,21 @@ handle_userauth_request(#ssh_msg_useraut
 handle_userauth_request(#ssh_msg_userauth_request{user = User,
 						  service = "ssh-connection",
 						  method = "none"}, _,
-			#ssh{userauth_supported_methods = Methods} = Ssh) ->
-    {not_authorized, {User, undefined},
-     {#ssh_msg_userauth_failure{authentications = Methods,
-                                partial_success = false}, Ssh}
-    };
+			#ssh{userauth_supported_methods = Methods,
+                             opts = Opts} = Ssh) ->
+    case ?GET_OPT(no_auth_needed, Opts) of
+        false ->
+            %% The normal case
+            {not_authorized, {User, undefined},
+             {#ssh_msg_userauth_failure{authentications = Methods,
+                                        partial_success = false}, Ssh}
+            };
+        true ->
+            %% RFC 4252  5.2
+	    {authorized, User,
+             {#ssh_msg_userauth_success{}, Ssh}
+            }
+    end;
 
 handle_userauth_request(#ssh_msg_userauth_request{user = User,
 						  service = "ssh-connection",
@@ -502,7 +514,7 @@ check_password(User, Password, #ssh{opts
 
 	undefined ->
 	    Static = get_password_option(Opts, User),
-	    {crypto:equal_const_time(Password,Static), Ssh};
+	    {ssh_lib:comp(Password,Static), Ssh};
 
 	Checker when is_function(Checker,2) ->
 	    {Checker(User, Password), Ssh};
@@ -545,13 +557,16 @@ pre_verify_sig(User, KeyBlob,  #ssh{opts
 verify_sig(SessionId, User, Service, AlgBin, KeyBlob, SigWLen, #ssh{opts=Opts} = Ssh) ->
     try
         Alg = binary_to_list(AlgBin),
+        true = lists:member(list_to_existing_atom(Alg), 
+                            proplists:get_value(public_key,
+                                                ?GET_OPT(preferred_algorithms,Opts))),
         Key = ssh_message:ssh2_pubkey_decode(KeyBlob), % or exception
         true = ssh_transport:call_KeyCb(is_auth_key, [Key, User], Opts),
         PlainText = build_sig_data(SessionId, User, Service, KeyBlob, Alg),
         <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen,
         <<?UINT32(AlgLen), _Alg:AlgLen/binary,
           ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig,
-        ssh_transport:verify(PlainText, ssh_transport:sha(Alg), Sig, Key, Ssh)
+        ssh_transport:verify(PlainText, list_to_existing_atom(Alg), Sig, Key, Ssh)
     catch
 	_:_ ->
 	    false
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_bits.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_bits.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_bits.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -40,20 +40,20 @@ mpint(I) when I>0 ->
     <<B1,V/binary>> = binary:encode_unsigned(I),
     case B1 band 16#80 of
 	16#80 ->
-	    <<(size(V)+2):32/unsigned-big-integer, 0,B1,V/binary >>;
+	    <<(byte_size(V)+2):32/unsigned-big-integer, 0,B1,V/binary >>;
 	_ ->
-	    <<(size(V)+1):32/unsigned-big-integer, B1,V/binary >>
+	    <<(byte_size(V)+1):32/unsigned-big-integer, B1,V/binary >>
     end;
 mpint(N) when N<0 -> 
-    Sxn =  8*size(binary:encode_unsigned(-N)),
+    Sxn =  bit_size(binary:encode_unsigned(-N)),
     Sxn1 = Sxn+8,
     <<W:Sxn1>> = <<1, 0:Sxn>>,
     <<B1,V/binary>> = binary:encode_unsigned(W+N),
     case B1 band 16#80 of
 	16#80 ->
-	    <<(size(V)+1):32/unsigned-big-integer, B1,V/binary >>;
+	    <<(byte_size(V)+1):32/unsigned-big-integer, B1,V/binary >>;
 	_ ->
-	    <<(size(V)+2):32/unsigned-big-integer, 255,B1,V/binary >>
+	    <<(byte_size(V)+2):32/unsigned-big-integer, 255,B1,V/binary >>
     end.
 
 %%%----------------------------------------------------------------
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_channel_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_channel_sup.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_channel_sup.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -40,35 +40,17 @@ start_link(Args) ->
 
 
 start_child(client, ChannelSup, ConnRef, Callback, Id, Args, Exec, _Opts) when is_pid(ConnRef) ->
-    start_the_child(ssh_client_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
+    start_the_channel(ssh_client_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
+
 start_child(server, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts) when is_pid(ConnRef) ->
      case max_num_channels_not_exceeded(ChannelSup, Opts) of
          true ->
-             start_the_child(ssh_server_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
+             start_the_channel(ssh_server_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
          false ->
              {error, max_num_channels_exceeded}
     end.
 
 
-start_the_child(ChanMod, ChannelSup, ConnRef, Callback, Id, Args, Exec) ->
-    ChildSpec =
-        #{id       => make_ref(),
-          start    => {ChanMod, start_link, [ConnRef, Id, Callback, Args, Exec]},
-          restart  => temporary,
-          type     => worker,
-          modules  => [ChanMod]
-         },
-    case supervisor:start_child(ChannelSup, ChildSpec) of
-        {ok, Pid} ->
-            {ok, Pid};
-        {ok, Pid, _Info} ->
-            {ok,Pid};
-        {error, {Error,_Info}} ->
-            {error, Error};
-        {error, Error} ->
-            {error, Error}
-    end.
-
 %%%=========================================================================
 %%%  Supervisor callback
 %%%=========================================================================
@@ -88,3 +70,20 @@ max_num_channels_not_exceeded(ChannelSup
 				   supervisor:which_children(ChannelSup)]),
     %% Note that NumChannels is BEFORE starting a new one
     NumChannels < MaxNumChannels.
+
+
+start_the_channel(ChanMod, ChannelSup, ConnRef, Callback, Id, Args, Exec) ->
+    ChildSpec =
+        #{id       => make_ref(),
+          start    => {ChanMod, start_link, [ConnRef, Id, Callback, Args, Exec]},
+          restart  => temporary,
+          type     => worker,
+          modules  => [ChanMod]
+         },
+    case supervisor:start_child(ChannelSup, ChildSpec) of
+        {ok, Pid} ->              {ok, Pid};
+        {ok, Pid, _Info} ->       {ok, Pid};
+        {error, {Error,_Info}} -> {error, Error};
+        {error, Error} ->         {error, Error}
+    end.
+
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_cli.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_cli.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_cli.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -79,7 +79,7 @@ handle_ssh_msg({ssh_cm, _ConnectionHandl
 	       #state{group = Group} = State0) ->
     {Enc, State} = guess_encoding(Data, State0),
     List = unicode:characters_to_list(Data, Enc),
-    to_group(List, Group),
+    to_group(List, Group, get_dumb(State#state.pty)),
     {ok, State};
 
 handle_ssh_msg({ssh_cm, ConnectionHandler,
@@ -281,6 +281,10 @@ handle_msg({Group, get_unicode_state}, S
     Group ! {self(), get_unicode_state, false},
     {ok, State};
 
+handle_msg({Group, get_terminal_state}, State) ->
+    Group ! {self(), get_terminal_state, true},
+    {ok, State};
+
 handle_msg({Group, tty_geometry}, #state{group = Group,
 					 pty = Pty
 					} = State) ->
@@ -389,21 +393,28 @@ out_enc(#state{encoding = PeerEnc,
 
 %%--------------------------------------------------------------------
 
-to_group([], _Group) ->
+to_group([], _Group, _Dumb) ->
     ok;
-to_group([$\^C | Tail], Group) ->
+to_group([$\^C | Tail], Group, Dumb) ->
     exit(Group, interrupt),
-    to_group(Tail, Group);
-to_group(Data, Group) ->
+    to_group(Tail, Group, Dumb);
+to_group(Data, Group, Dumb) ->
     Func = fun(C) -> C /= $\^C end,
     Tail = case lists:splitwith(Func, Data) of
         {[], Right} ->
             Right;
         {Left, Right} ->
-            Group ! {self(), {data, Left}},
+            %% Filter out escape sequences, only support Ctrl sequences
+            Left1 = if Dumb -> replace_escapes(Left); true -> Left end,
+            Group ! {self(), {data, Left1}},
             Right
     end,
-    to_group(Tail, Group).
+    to_group(Tail, Group, Dumb).
+replace_escapes(Data) ->
+    lists:flatten([ if C =:= 27 ->
+        [$^,C+64];
+         true -> C
+    end || C <- Data]).
 
 %%--------------------------------------------------------------------
 %%% io_request, handle io requests from the user process,
@@ -420,14 +431,44 @@ io_request({put_chars, Cs}, Buf, Tty, _G
     put_chars(bin_to_list(Cs), Buf, Tty);
 io_request({put_chars, unicode, Cs}, Buf, Tty, _Group) ->
     put_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty);
+io_request({put_expand_no_trim, unicode, Expand}, Buf, Tty, _Group) ->
+    insert_chars(unicode:characters_to_list(Expand, unicode), Buf, Tty);
 io_request({insert_chars, Cs}, Buf, Tty, _Group) ->
     insert_chars(bin_to_list(Cs), Buf, Tty);
 io_request({insert_chars, unicode, Cs}, Buf, Tty, _Group) ->
     insert_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty);
 io_request({move_rel, N}, Buf, Tty, _Group) ->
     move_rel(N, Buf, Tty);
+io_request({move_line, N}, Buf, Tty, _Group) ->
+    move_line(N, Buf, Tty);
+io_request({move_combo, L, V, R}, Buf, Tty, _Group) ->
+    {ML, Buf1} = move_rel(L, Buf, Tty),
+    {MV, Buf2} = move_line(V, Buf1, Tty),
+    {MR, Buf3} = move_rel(R, Buf2, Tty),
+    {[ML,MV,MR], Buf3};
+io_request(new_prompt, _Buf, _Tty, _Group) ->
+    {[], {[], {[],[]}, [], 0 }};
+io_request(delete_line, {_, {_, _}, _, Col}, Tty, _Group) ->
+    MoveToBeg = move_cursor(Col, 0, Tty),
+    {[MoveToBeg, "\e[J"],
+     {[],{[],[]},[],0}};
+io_request({redraw_prompt, Pbs, Pbs2, {LB, {Bef, Aft}, LA}}, Buf, Tty, _Group) ->
+    {ClearLine, Cleared} = io_request(delete_line, Buf, Tty, _Group),
+    CL = lists:reverse(Bef,Aft),
+    Text = Pbs ++ lists:flatten(lists:join("\n"++Pbs2, lists:reverse(LB)++[CL|LA])),
+    Moves = if LA /= [] ->
+                    [Last|_] = lists:reverse(LA),
+                    {move_combo, -length(Last), -length(LA), length(Bef)};
+               true ->
+                    {move_rel, -length(Aft)}
+            end,
+    {T, InsertedText} = io_request({insert_chars, unicode:characters_to_binary(Text)}, Cleared, Tty, _Group),
+    {M, Moved} = io_request(Moves, InsertedText, Tty, _Group),
+    {[ClearLine, T, M], Moved};
 io_request({delete_chars,N}, Buf, Tty, _Group) ->
     delete_chars(N, Buf, Tty);
+io_request(clear, Buf, _Tty, _Group) ->
+    {"\e[H\e[2J", Buf};
 io_request(beep, Buf, _Tty, _Group) ->
     {[7], Buf};
 
@@ -441,13 +482,12 @@ io_request({requests,Rs}, Buf, Tty, Grou
 io_request(tty_geometry, Buf, Tty, Group) ->
     io_requests([{move_rel, 0}, {put_chars, unicode, [10]}],
                 Buf, Tty, [], Group);
-     %{[], Buf};
 
 %% New in 18
 io_request({put_chars_sync, Class, Cs, Reply}, Buf, Tty, Group) ->
     %% We handle these asynchronous for now, if we need output guarantees
     %% we have to handle these synchronously
-    Group ! {reply, Reply},
+    Group ! {reply, Reply, ok},
     io_request({put_chars, Class, Cs}, Buf, Tty, Group);
 
 io_request(_R, Buf, _Tty, _Group) ->
@@ -478,57 +518,67 @@ get_tty_command(left, N, _TerminalType)
 -define(TABWIDTH, 8).
 
 %% convert input characters to buffer and to writeout
-%% Note that the buf is reversed but the buftail is not
+%% Note that Bef is reversed but Aft is not
 %% (this is handy; the head is always next to the cursor)
-conv_buf([], AccBuf, AccBufTail, AccWrite, Col, _Tty) ->
-    {AccBuf, AccBufTail, lists:reverse(AccWrite), Col};
-conv_buf([13, 10 | Rest], _AccBuf, AccBufTail, AccWrite, _Col, Tty) ->
-    conv_buf(Rest, [], tl2(AccBufTail), [10, 13 | AccWrite], 0, Tty);
-conv_buf([13 | Rest], _AccBuf, AccBufTail, AccWrite, _Col, Tty) ->
-    conv_buf(Rest, [], tl1(AccBufTail), [13 | AccWrite], 0, Tty);
-conv_buf([10 | Rest], _AccBuf, AccBufTail, AccWrite0, _Col, Tty) ->
+conv_buf([], {LB, {Bef, Aft}, LA, Col}, AccWrite, _Tty) ->
+    {{LB, {Bef, Aft}, LA, Col}, lists:reverse(AccWrite)};
+conv_buf([13, 10 | Rest], {LB, {Bef, Aft}, LA, Col}, AccWrite, Tty = #ssh_pty{width = W}) ->
+    conv_buf(Rest, {[lists:reverse(Bef)|LB], {[], tl2(Aft)}, LA, Col+(W-(Col rem W))}, [10, 13 | AccWrite], Tty);
+conv_buf([13 | Rest], {LB, {Bef, Aft}, LA, Col}, AccWrite, Tty = #ssh_pty{width = W}) ->
+    conv_buf(Rest, {[lists:reverse(Bef)|LB], {[], tl1(Aft)}, LA, Col+(W-(Col rem W))}, [13 | AccWrite], Tty);
+conv_buf([10 | Rest],{LB, {Bef, Aft}, LA, Col}, AccWrite0, Tty = #ssh_pty{width = W}) ->
     AccWrite =
         case pty_opt(onlcr,Tty) of
             0 -> [10 | AccWrite0];
             1 -> [10,13 | AccWrite0];
             undefined -> [10 | AccWrite0]
         end,
-    conv_buf(Rest, [], tl1(AccBufTail), AccWrite, 0, Tty);
-conv_buf([C | Rest], AccBuf, AccBufTail, AccWrite, Col, Tty) ->
-    conv_buf(Rest, [C | AccBuf], tl1(AccBufTail), [C | AccWrite], Col + 1, Tty).
-
-
-%%% put characters at current position (possibly overwriting
-%%% characters after current position in buffer)
-put_chars(Chars, {Buf, BufTail, Col}, Tty) ->
-    {NewBuf, NewBufTail, WriteBuf, NewCol} =
-	conv_buf(Chars, Buf, BufTail, [], Col, Tty),
-    {WriteBuf, {NewBuf, NewBufTail, NewCol}}.
+    conv_buf(Rest, {[lists:reverse(Bef)|LB], {[], tl1(Aft)}, LA, Col+(W - (Col rem W))}, AccWrite, Tty);
+conv_buf([C | Rest], {LB, {Bef, Aft}, LA, Col}, AccWrite, Tty) ->
+    conv_buf(Rest, {LB, {[C|Bef], tl1(Aft)}, LA, Col+1}, [C | AccWrite], Tty).
+
+%%% put characters before the prompt
+put_chars(Chars, Buf, Tty) ->
+    Dumb = get_dumb(Tty),
+    case Buf of
+        {[],{[],[]},[],_} -> {_, WriteBuf} = conv_buf(Chars, Buf, [], Tty),
+            {WriteBuf, Buf};
+        _ when Dumb =:= false ->
+            {Delete, DeletedState} = io_request(delete_line, Buf, Tty, []),
+            {_, PutBuffer} = conv_buf(Chars, DeletedState, [], Tty),
+            {Redraw, _} = io_request(redraw_prompt_pre_deleted, Buf, Tty, []),
+            {[Delete, PutBuffer, Redraw], Buf};
+        _ ->
+            %% When we have a dumb terminal, we get messages via put_chars requests
+            %% so state should be empty {[],{[],[]},[],_},
+            %% but if we end up here its not, so keep the state
+            {_, WriteBuf} = conv_buf(Chars, Buf, [], Tty),
+            {WriteBuf, Buf}
+    end.
 
 %%% insert character at current position
-insert_chars([], {Buf, BufTail, Col}, _Tty) ->
-    {[], {Buf, BufTail, Col}};
-insert_chars(Chars, {Buf, BufTail, Col}, Tty) ->
-    {NewBuf, _NewBufTail, WriteBuf, NewCol} =
-	conv_buf(Chars, Buf, [], [], Col, Tty),
-    M = move_cursor(special_at_width(NewCol+length(BufTail), Tty), NewCol, Tty),
-    {[WriteBuf, BufTail | M], {NewBuf, BufTail, NewCol}}.
+insert_chars([], Buf, _Tty) ->
+    {[], Buf};
+insert_chars(Chars, {_LB,{_Bef, Aft},LA, _Col}=Buf, Tty) ->
+    {{NewLB, {NewBef, _NewAft}, _NewLA, NewCol}, WriteBuf} = conv_buf(Chars, Buf, [], Tty),
+    M = move_cursor(special_at_width(NewCol+length(Aft), Tty), NewCol, Tty),
+    {[WriteBuf, Aft | M], {NewLB,{NewBef, Aft},LA, NewCol}}.
 
 %%% delete characters at current position, (backwards if negative argument)
-delete_chars(0, {Buf, BufTail, Col}, _Tty) ->
-    {[], {Buf, BufTail, Col}};
-delete_chars(N, {Buf, BufTail, Col}, Tty) when N > 0 ->
-    NewBufTail = nthtail(N, BufTail),
-    M = move_cursor(Col + length(NewBufTail) + N, Col, Tty),
-    {[NewBufTail, lists:duplicate(N, $ ) | M],
-     {Buf, NewBufTail, Col}};
-delete_chars(N, {Buf, BufTail, Col}, Tty) -> % N < 0
-    NewBuf = nthtail(-N, Buf),
+delete_chars(0, {LB,{Bef, Aft},LA, Col}, _Tty) ->
+    {[], {LB,{Bef, Aft},LA, Col}};
+delete_chars(N, {LB,{Bef, Aft},LA, Col}, Tty) when N > 0 ->
+    NewAft = nthtail(N, Aft),
+    M = move_cursor(Col + length(NewAft) + N, Col, Tty),
+    {[NewAft, lists:duplicate(N, $ ) | M],
+     {LB,{Bef, NewAft},LA, Col}};
+delete_chars(N, {LB,{Bef, Aft},LA, Col}, Tty) -> % N < 0
+    NewBef = nthtail(-N, Bef),
     NewCol = case Col + N of V when V >= 0 -> V; _ -> 0 end,
     M1 = move_cursor(Col, NewCol, Tty),
-    M2 = move_cursor(special_at_width(NewCol+length(BufTail)-N, Tty), NewCol, Tty),
-    {[M1, BufTail, lists:duplicate(-N, $ ) | M2],
-     {NewBuf, BufTail, NewCol}}.
+    M2 = move_cursor(special_at_width(NewCol+length(Aft)-N, Tty), NewCol, Tty),
+    {[M1, Aft, lists:duplicate(-N, $ ) | M2],
+     {LB,{NewBef, Aft},LA, NewCol}}.
 
 %%% Window change, redraw the current line (and clear out after it
 %%% if current window is wider than previous)
@@ -536,52 +586,74 @@ window_change(Tty, OldTty, Buf)
   when OldTty#ssh_pty.width == Tty#ssh_pty.width ->
      %% No line width change
     {[], Buf};
-window_change(Tty, OldTty, {Buf, BufTail, Col}) ->
+window_change(Tty, OldTty, {LB, {Bef, Aft}, LA, Col}) ->
     case OldTty#ssh_pty.width - Tty#ssh_pty.width of
         0 ->
             %% No line width change
-            {[], {Buf,BufTail,Col}};
+            {[], {LB, {Bef, Aft}, LA, Col}};
 
         DeltaW0 when DeltaW0 < 0,
-                     BufTail == [] ->
+                     Aft == [] ->
             % Line width is decreased, cursor is at end of input
-            {[], {Buf,BufTail,Col}};
+            {[], {LB, {Bef, Aft}, LA, Col}};
 
         DeltaW0 when DeltaW0 < 0,
-                     BufTail =/= [] ->
+                     Aft =/= [] ->
             % Line width is decreased, cursor is not at end of input
-            {[], {Buf,BufTail,Col}};
+            {[], {LB, {Bef, Aft}, LA, Col}};
 
         DeltaW0 when DeltaW0 > 0 ->
             % Line width is increased
-            {[], {Buf,BufTail,Col}}
+            {[], {LB, {Bef, Aft}, LA, Col}}
         end.
 
 %% move around in buffer, respecting pad characters
-step_over(0, Buf, [?PAD | BufTail], Col) ->
-    {[?PAD | Buf], BufTail, Col+1};
-step_over(0, Buf, BufTail, Col) ->
-    {Buf, BufTail, Col};
-step_over(N, [C | Buf], BufTail, Col) when N < 0 ->
+step_over(0, {LB, {Bef, [?PAD |Aft]}, LA, Col}) ->
+    {LB, {[?PAD | Bef], Aft}, LA, Col+1};
+step_over(0, {LB, {Bef, Aft}, LA, Col}) ->
+    {LB, {Bef, Aft}, LA, Col};
+step_over(N, {LB, {[C | Bef], Aft}, LA, Col}) when N < 0 ->
     N1 = ifelse(C == ?PAD, N, N+1),
-    step_over(N1, Buf, [C | BufTail], Col-1);
-step_over(N, Buf, [C | BufTail], Col) when N > 0 ->
+    step_over(N1, {LB, {Bef, [C | Aft]}, LA, Col-1});
+step_over(N, {LB, {Bef, [C | Aft]}, LA, Col}) when N > 0 ->
     N1 = ifelse(C == ?PAD, N, N-1),
-    step_over(N1, [C | Buf], BufTail, Col+1).
+    step_over(N1, {LB, {[C | Bef], Aft}, LA, Col+1}).
 
 %%% an empty line buffer
-empty_buf() -> {[], [], 0}.
+empty_buf() -> {[], {[], []}, [], 0}.
 
 %%% col and row from position with given width
 col(N, W) -> N rem W.
 row(N, W) -> N div W.
 
 %%% move relative N characters
-move_rel(N, {Buf, BufTail, Col}, Tty) ->
-    {NewBuf, NewBufTail, NewCol} = step_over(N, Buf, BufTail, Col),
+move_rel(N, {_LB, {_Bef, _Aft}, _LA, Col}=Buf, Tty) ->
+    {NewLB, {NewBef, NewAft}, NewLA, NewCol} = step_over(N, Buf),
     M = move_cursor(Col, NewCol, Tty),
-    {M, {NewBuf, NewBufTail, NewCol}}.
+    {M, {NewLB, {NewBef, NewAft}, NewLA, NewCol}}.
 
+move_line(V, {_LB, {_Bef, _Aft}, _LA, Col}, Tty = #ssh_pty{width=W})
+        when V < 0, length(_LB) >= -V ->
+    {LinesJumped, [B|NewLB]} = lists:split(-V -1, _LB),
+    CL = lists:reverse(_Bef,_Aft),
+    NewLA = lists:reverse([CL|LinesJumped], _LA),
+    {NewBB, NewAft} = lists:split(min(length(_Bef),length(B)), B),
+    NewBef = lists:reverse(NewBB),
+    NewCol = Col - length(_Bef) - lists:sum([((length(L)-1) div W)*W + W || L <- [B|LinesJumped]]) + length(NewBB),
+    M = move_cursor(Col, NewCol, Tty),
+    {M, {NewLB, {NewBef, NewAft}, NewLA, NewCol}};
+move_line(V, {_LB, {_Bef, _Aft}, _LA, Col}, Tty = #ssh_pty{width=W})
+        when V > 0, length(_LA) >= V ->
+    {LinesJumped, [A|NewLA]} = lists:split(V -1, _LA),
+    CL = lists:reverse(_Bef,_Aft),
+    NewLB = lists:reverse([CL|LinesJumped],_LB),
+    {NewBB, NewAft} = lists:split(min(length(_Bef),length(A)), A),
+    NewBef = lists:reverse(NewBB),
+    NewCol = Col - length(_Bef) + lists:sum([((length(L)-1) div W)*W + W || L <- [CL|LinesJumped]]) + length(NewBB),
+    M = move_cursor(Col, NewCol, Tty),
+    {M, {NewLB, {NewBef, NewAft}, NewLA, NewCol}};
+move_line(_, Buf, _) ->
+    {"", Buf}.
 %%% give move command for tty
 move_cursor(A, A, _Tty) ->
     [];
@@ -666,7 +738,9 @@ start_shell(ConnectionHandler, State) ->
             {_,_,_} = Shell ->
                 Shell
         end,
-    State#state{group = group:start(self(), ShellSpawner, [{echo, get_echo(State#state.pty)}]),
+    State#state{group = group:start(self(), ShellSpawner,
+                                    [{dumb, get_dumb(State#state.pty)},{expand_below, false},
+                                     {echo, get_echo(State#state.pty)}]),
                 buf = empty_buf()}.
 
 %%--------------------------------------------------------------------
@@ -687,7 +761,8 @@ start_exec_shell(ConnectionHandler, Cmd,
             {M,F,A} ->
                 {M, F, A++[Cmd]}
         end,
-    State#state{group = group:start(self(), ExecShellSpawner, [{echo,false}]),
+    State#state{group = group:start(self(), ExecShellSpawner, [{expand_below, false},
+                                                               {echo,false}]),
                 buf = empty_buf()}.
 
 %%--------------------------------------------------------------------
@@ -771,7 +846,8 @@ exec_in_self_group(ConnectionHandler, Ch
                           end
                   end)
         end,
-    {ok, State#state{group = group:start(self(), Exec, [{echo,false}]),
+    {ok, State#state{group = group:start(self(), Exec, [{expand_below, false},
+                                                        {echo,false}]),
                      buf = empty_buf()}}.
     
 
@@ -780,6 +856,13 @@ t2str(T) -> try io_lib:format("~s",[T])
             end.
 
 %%--------------------------------------------------------------------
+get_dumb(Tty) ->
+    try
+        Tty#ssh_pty.term =:= "dumb"
+    catch
+        _:_ -> false
+    end.
+
 % Pty can be undefined if the client never sets any pty options before
 % starting the shell.
 get_echo(Tty) ->
@@ -923,4 +1006,3 @@ fmt_kv1({K,h,V}) -> io_lib:format("~n~p:
 type(0) -> "0 (normal data)";
 type(1) -> "1 (extended data, i.e. errors)";
 type(T) -> T.
-
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_client_channel.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_client_channel.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_client_channel.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -70,7 +70,8 @@
 -export([cache_create/0, cache_lookup/2, cache_update/2, 
 	 cache_delete/1, cache_delete/2,  cache_foldl/3,
 	 cache_info/2,  cache_find/2,
-	 get_print_info/1]).
+	 get_print_info/1, get_print_info/2
+        ]).
 
 -behaviour(ssh_dbg).
 -export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
@@ -212,6 +213,9 @@ handle_call(get_print_info, _From, State
 	},
     {reply, Reply, State};
 
+handle_call({get_print_info,channel_cb}, _From, State) ->
+    {reply, State#state.channel_cb, State};
+
 handle_call(Request, From, #state{channel_cb = Module, 
 				  channel_state = ChannelState} = State) ->
    try Module:handle_call(Request, From, ChannelState) of
@@ -257,10 +261,9 @@ handle_info({ssh_cm, ConnectionManager,
     (catch ssh_connection:close(ConnectionManager, ChannelId)),
     {stop, normal, State#state{close_sent = true}};
 
-handle_info({ssh_cm, _, _} = Msg, #state{cm = ConnectionManager,
-			channel_cb = Module, 
-			channel_state = ChannelState0} = State) ->
-    case Module:handle_ssh_msg(Msg, ChannelState0) of
+handle_info({ssh_cm, _, _} = Msg, #state{channel_cb = Module, 
+                                         channel_state = ChannelState0} = State) ->
+    try Module:handle_ssh_msg(Msg, ChannelState0) of
 	{ok, ChannelState} ->
 	    adjust_window(Msg),
 	    {noreply, State#state{channel_state = ChannelState}};
@@ -268,34 +271,39 @@ handle_info({ssh_cm, _, _} = Msg, #state
 	    adjust_window(Msg),
 	    {noreply, State#state{channel_state = ChannelState}, Timeout};
 	{stop, ChannelId, ChannelState} ->
-	    catch ssh_connection:close(ConnectionManager, ChannelId),
-	    {stop, normal, State#state{close_sent = true,
-				       channel_state = ChannelState}}
+            do_the_close(Msg, ChannelId, State#state{channel_state = ChannelState})
+    catch
+        error:_ ->
+            do_the_close(Msg, State#state.channel_id, State)
     end;
 
-handle_info(Msg, #state{cm = ConnectionManager, channel_cb = Module, 
+handle_info(Msg, #state{channel_cb = Module, 
 			channel_state = ChannelState0} = State) -> 
-    case Module:handle_msg(Msg, ChannelState0) of 
+    try Module:handle_msg(Msg, ChannelState0)
+    of 
 	{ok, ChannelState} ->
 	    {noreply, State#state{channel_state = ChannelState}};
 	{ok, ChannelState, Timeout} ->
 	    {noreply, State#state{channel_state = ChannelState}, Timeout};
-	{stop, Reason, ChannelState} when is_atom(Reason)->
-	    {stop, Reason, State#state{close_sent = true,
-				       channel_state = ChannelState}};
 	{stop, ChannelId, ChannelState} ->
-	    Reason =
-		case Msg of
-		    {'EXIT', _Pid, shutdown} ->
-			shutdown;
-		    _ ->
-			normal
-		end,
-	    (catch ssh_connection:close(ConnectionManager, ChannelId)),
-	    {stop, Reason, State#state{close_sent = true,
-				       channel_state = ChannelState}}
+            do_the_close(Msg, ChannelId, State#state{channel_state = ChannelState})
+    catch
+        error:function_clause when tuple_size(Msg) == 3,
+                                   element(1,Msg) == 'EXIT' ->
+            do_the_close(Msg, State#state.channel_id, State)
     end.
 
+
+
+do_the_close(Msg, ChannelId, State = #state{close_sent = false,
+                                            cm = ConnectionManager}) ->
+    catch ssh_connection:close(ConnectionManager, ChannelId),
+    do_the_close(Msg, ChannelId, State#state{close_sent=true});
+do_the_close({'EXIT', _Pid, shutdown=Reason},     _, State) -> {stop, Reason, State};
+do_the_close({'EXIT', _Pid, {shutdown,_}=Reason}, _, State) -> {stop, Reason, State};
+do_the_close(_Msg,                                _, State) -> {stop, normal, State}.
+
+
 %%--------------------------------------------------------------------
 %% Function: terminate(Reason, State) -> void()
 %% Description: This function is called by a gen_server when it is about to
@@ -361,6 +369,9 @@ cache_find(ChannelPid, Cache) ->
 get_print_info(Pid) ->
     call(Pid, get_print_info, 1000).
 
+get_print_info(Pid, Arg) ->
+    call(Pid, {get_print_info,Arg}, 1000).
+
 %%--------------------------------------------------------------------
 %%% Internal functions
 %%--------------------------------------------------------------------
@@ -379,7 +390,7 @@ handle_cb_result({stop, Reason, ChannelS
 
 adjust_window({ssh_cm, ConnectionManager,
 	       {data, ChannelId, _, Data}}) ->
-    ssh_connection:adjust_window(ConnectionManager, ChannelId, size(Data));
+    ssh_connection:adjust_window(ConnectionManager, ChannelId, byte_size(Data));
 adjust_window(_) ->
     ok.
     
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_client_key_api.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_client_key_api.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_client_key_api.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2011-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -82,7 +82,7 @@
 %%% in the argument Host with the port Port.
 %%%
 %%% Due to compatibility reasons, the OTP/SSH application first
-%%% trys add_host_key/4 and then the old add_host_key/3
+%%% tries add_host_key/4 and then the old add_host_key/3
 
 -callback add_host_key(Host :: inet:ip_address() | inet:hostname()
                              | [inet:ip_address() | inet:hostname()],
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_daemon_channel.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_daemon_channel.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_daemon_channel.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2022. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@
 
 -module(ssh_daemon_channel).
 
-%% API to server side channel that can be pluged into the erlang ssh daemeon
+%% API to server side channel that can be plugged into the erlang ssh daemeon
 -callback init(Args :: term()) ->
     {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} |
     {stop, Reason :: term()} | ignore.
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_dbg.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_dbg.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_dbg.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -113,7 +113,7 @@ start(IoFmtFun) when is_function(IoFmtFu
 
 stop() ->
     try
-        dbg:stop_clear(),
+        dbg:stop(),
         gen_server:stop(?SERVER)
     catch
         _:_ -> ok
@@ -156,26 +156,26 @@ go_on() ->
     on(IsOn).
 
 %%%----------------------------------------------------------------
-shrink_bin(B) when is_binary(B), size(B)>256 -> {'*** SHRINKED BIN',
-						 size(B),
-						 element(1,split_binary(B,64)),
-						 '...',
-						 element(2,split_binary(B,size(B)-64))
-						};
+shrink_bin(B) when is_binary(B), byte_size(B)>256 -> {'*** SHRUNK BIN',
+                                              byte_size(B),
+                                              element(1,split_binary(B,64)),
+                                              '...',
+                                              element(2,split_binary(B,byte_size(B)-64))
+                                             };
 shrink_bin(L) when is_list(L) -> lists:map(fun shrink_bin/1, L);
 shrink_bin(T) when is_tuple(T) -> list_to_tuple(shrink_bin(tuple_to_list(T)));
 shrink_bin(X) -> X.
 
 %%%----------------------------------------------------------------    
-%% Replace any occurence of {Name,...}, with "#Name{}"
+%% Replace any occurrence of {Name,...}, with "#Name{}"
 reduce_state(T, RecordExample) ->
     Name = element(1, RecordExample),
-    Arity = size(RecordExample),
+    Arity = tuple_size(RecordExample),
     reduce_state(T, Name, Arity).
 
-%% Replace any occurence of {Name,...}, with "#Name{}"
+%% Replace any occurrence of {Name,...}, with "#Name{}"
 reduce_state(T, Name, Arity) when element(1,T) == Name,
-                                  size(T) == Arity ->
+                                  tuple_size(T) == Arity ->
     lists:concat(['#',Name,'{}']);
 reduce_state(L, Name, Arity) when is_list(L) ->
     [reduce_state(E,Name,Arity) || E <- L];
@@ -353,7 +353,7 @@ trace_pid(T) when element(1,T)==trace
 
 %% Pick last element, the Time Stamp, and format it
 trace_ts(T) when  element(1,T)==trace_ts ->
-    ts( element(size(T), T) ).
+    ts( element(tuple_size(T), T) ).
 
 %% Make a tuple of all elements but the 1st, 2nd and last
 trace_info(T) ->
@@ -400,7 +400,7 @@ try_all_types_in_all_modules(TypesOn, Ar
                                     catch
                                         _:_ ->
                                             %% and finally, signal for special formatting
-                                            %% if noone else formats it
+                                            %% if no one else formats it
                                             Acc
                                     end
                             end
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_file.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_file.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_file.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -30,7 +30,8 @@
 -include("ssh.hrl").
 
 %% experimental:
--export([decode_ssh_file/4]).
+-export([decode_ssh_file/4
+        ]).
 
 %%%--------------------- server exports ---------------------------
 -behaviour(ssh_server_key_api).
@@ -44,10 +45,16 @@
 -export_type([pubkey_passphrase_client_options/0]).
 -type pubkey_passphrase_client_options() ::   {dsa_pass_phrase,      string()}
                                             | {rsa_pass_phrase,      string()}
-%% Not yet implemented:                     | {ed25519_pass_phrase,  string()}
-%% Not yet implemented:                     | {ed448_pass_phrase,    string()}
+                                              %% Not yet implemented:                     | {ed25519_pass_phrase,  string()}
+                                              %% Not yet implemented:                     | {ed448_pass_phrase,    string()}
                                             | {ecdsa_pass_phrase,    string()} .
 
+%%%--------------------- utility exports ---------------------------
+-export([decode/2, encode/2]).
+-export([extract_public_key/1]).
+
+-define(ENCODED_LINE_LENGTH, 68).
+
 %%%--------------------- common exports ---------------------------
 -export_type([user_dir_common_option/0,
               user_dir_fun_common_option/0
@@ -59,6 +66,10 @@
 
 -type optimize_key_lookup() :: {optimize, time|space} .
 
+-type key() :: public_key:public_key() | public_key:private_key() .
+-type experimental_openssh_key_v1() :: [{key(), openssh_key_v1_attributes()}].
+-type openssh_key_v1_attributes() :: [{atom(),term()}].
+
 %%%================================================================
 %%%
 %%% API
@@ -87,7 +98,7 @@ is_auth_key(Key0, User, Opts) ->
     Key = encode_key(Key0),
     lookup_auth_keys(KeyType, Key, filename:join(Dir,"authorized_keys"), Opts)
         orelse
-    lookup_auth_keys(KeyType, Key, filename:join(Dir,"authorized_keys2"), Opts).
+        lookup_auth_keys(KeyType, Key, filename:join(Dir,"authorized_keys2"), Opts).
 
 %%%---------------- CLIENT API ------------------------------------
 -spec user_key(Algorithm, Options) -> Result when
@@ -146,6 +157,267 @@ add_host_key(Hosts0, Port, Key, Opts) ->
 	    {error,{add_host_key,Error}}
     end.
 
+%%%---------------- UTILITY API -----------------------------------
+%%% In public key before OTP-24.0 as ssh_decode/2 and ssh_encode/2
+
+-spec decode(SshBin, Type) -> Decoded | {error,term()}
+                                  when SshBin :: binary(),
+                                       Type :: ssh2_pubkey
+                                             | public_key
+                                             | openssh_key
+                                             | rfc4716_key
+                                             | openssh_key_v1  % Experimental
+                                             | known_hosts
+                                             | auth_keys,
+                                       Decoded :: Decoded_ssh2_pubkey
+                                                | Decoded_public
+                                                | Decoded_openssh
+                                                | Decoded_rfc4716
+                                                | Decoded_openssh_key_v1
+                                                | Decoded_known_hosts
+                                                | Decoded_auth_keys,
+
+                                       Decoded_ssh2_pubkey :: public_key:public_key(),
+                                       Decoded_public :: Decoded_rfc4716
+                                                       | Decoded_openssh_key_v1
+                                                       | Decoded_openssh,
+                                       Decoded_openssh :: [{public_key:public_key(), [{comment,string()}]}],
+                                       Decoded_rfc4716 :: [{key(), [{headers,Attrs}]}],
+                                       Decoded_openssh_key_v1 :: experimental_openssh_key_v1(),
+                                       Decoded_known_hosts :: [{public_key:public_key(), [{comment,string()}
+                                                                                          | {hostnames,[string()]}]}],
+                                       Decoded_auth_keys :: [{public_key:public_key(), [{comment,string()}
+                                                                                        | {options,[string()]}]}],
+                                       Attrs :: {Key::string(), Value::string()} .
+
+decode(KeyBin, ssh2_pubkey) when is_binary(KeyBin) ->
+    ssh_message:ssh2_pubkey_decode(KeyBin);
+
+decode(KeyBin, public_key) when is_binary(KeyBin) ->
+    Type = case KeyBin of
+               <<"-----BEGIN OPENSSH",_/binary>> -> openssh_key_v1;
+               <<"----",_/binary>> -> rfc4716_key;
+               _ -> openssh_key
+           end,
+    decode(KeyBin, Type);
+
+decode(KeyBin, Type) when is_binary(KeyBin) andalso 
+                          (Type==rfc4716_key orelse
+                           Type==openssh_key_v1 % Experimental
+                          ) ->
+    %% Ex: <<"---- BEGIN SSH2 PUBLIC KEY ----\n....">>     (rfc4716_key)
+    %%     <<"-----BEGIN OPENSSH PRIVATE KEY-----\n....">> (openssh_key_v1)
+    case decode_ssh_file(public, any, KeyBin, ignore) of
+        {ok,Keys} ->
+            [{Key,
+              if
+                  Attrs =/= [] ->
+                      [{headers, [{binary_to_list(K),binary_to_list(V)} || {K,V} <- Attrs]}];
+                  Attrs == [] ->
+                      []
+              end
+             }
+             || {Key,Attrs} <- Keys];
+
+        {error,Error} ->
+            {error,Error}
+    end;
+
+decode(KeyBin0, openssh_key) when is_binary(KeyBin0) ->
+    %% Ex: <<"ssh-rsa AAAAB12....3BC someone@example.com">>
+    try
+        [begin
+             [_,K|Rest] = binary:split(Line, <<" ">>, [global,trim_all]),
+             Key = ssh_message:ssh2_pubkey_decode(base64:decode(K)),
+             case Rest of
+                 [Comment] -> {Key, [{comment,binary_to_list(Comment)}]};
+                 [] -> {Key,[]}
+             end
+         end || Line <- split_in_nonempty_lines(KeyBin0)
+        ]
+    catch
+        _:_ -> {error, key_decode_failed}
+    end;
+
+decode(Bin, known_hosts) when is_binary(Bin) ->
+    [begin
+         Attrs = 
+             [
+              {comment, binary_to_list(erlang:iolist_to_binary(lists:join(" ", Comment)))}
+              || Comment =/= []
+             ] ++
+             [
+              {hostnames,
+               [binary_to_list(HP)
+                || HP <- binary:split(HostPort,<<",">>,[global,trim_all])
+               ]}
+             ],
+         {ssh_message:ssh2_pubkey_decode(base64:decode(KeyBin)),
+          Attrs
+         }
+     end
+     || L <- split_in_nonempty_lines(Bin),
+        [HostPort,_KeyType,KeyBin|Comment] <- [binary:split(L,<<" ">>,[global,trim_all])]
+    ];
+
+decode(Bin, auth_keys) when is_binary(Bin) ->
+    [begin
+         Attrs = 
+             [
+              {comment, binary_to_list(erlang:iolist_to_binary(lists:join(" ", Comment)))}
+              || Comment =/= []
+             ] ++
+             [
+              {options, lists:map(fun erlang:binary_to_list/1, Options)}
+              || Options =/= []
+             ],
+         {ssh_message:ssh2_pubkey_decode(base64:decode(KeyBin)),
+          Attrs
+         }
+     end
+     || L <- split_in_nonempty_lines(Bin),
+        [Options,_KeyType,KeyBin|Comment] <-
+            case binary:match(L, [<<"ssh-rsa">>,
+                                  <<"rsa-sha2-">>,
+                                  <<"ssh-dss">>,
+                                  <<"ecdsa-sha2-nistp">>,
+                                  <<"ssh-ed">>
+                                 ]) of
+                nomatch ->
+                    [];
+                {0, Len} when is_integer(Len) ->
+                    [ [[] | binary:split(L,<<" ">>,[global,trim_all])] ];
+                {Pos,Len} when is_integer(Pos), is_integer(Len) ->
+                    [ [binary:split(binary:part(L,0,Pos-1), <<",">>,[global,trim_all]) |
+                       binary:split(binary:part(L,Pos,byte_size(L)-Pos), <<" ">>, [global,trim_all])]
+                    ]
+            end
+    ];
+
+decode(_KeyBin, _Type) ->
+    error(badarg).
+
+%%%----------------------------------------------------------------
+-spec encode(InData, Type) -> binary() | {error,term()}
+                                  when Type :: ssh2_pubkey
+                                             | openssh_key
+                                             | rfc4716_key
+                                             | openssh_key_v1  % Experimental
+                                             | known_hosts
+                                             | auth_keys,
+                                       InData :: InData_ssh2_pubkey
+                                               | InData_openssh
+                                               | InData_rfc4716
+                                               | InData_openssh_key_v1
+                                               | InData_known_hosts
+                                               | InData_auth_keys,
+
+                                       InData_ssh2_pubkey :: public_key:public_key(),
+                                       InData_openssh :: [{public_key:public_key(), [{comment,string()}]}],
+                                       InData_rfc4716 :: [{key(), [{headers,Attrs}]}],
+                                       InData_openssh_key_v1 :: experimental_openssh_key_v1(),
+                                       InData_known_hosts :: [{public_key:public_key(), [{comment,string()}
+                                                                                          | {hostnames,[string()]}]}],
+                                       InData_auth_keys :: [{public_key:public_key(), [{comment,string()}
+                                                                                        | {options,[string()]}]}],
+                                       Attrs :: {Key::string(), Value::string()} .
+
+encode(Key, ssh2_pubkey) ->
+    ssh_message:ssh2_pubkey_encode(Key);
+
+encode(KeyAttrs, Type) when Type==rfc4716_key ;
+                            Type==openssh_key_v1 % Experimental
+                            ->
+    {Begin, End, F} =
+        case Type of
+            rfc4716_key ->
+                {"---- BEGIN SSH2 PUBLIC KEY ----\n",
+                 "---- END SSH2 PUBLIC KEY ----\n",
+                 fun ssh_message:ssh2_pubkey_encode/1};
+            openssh_key_v1 ->
+                {"-----BEGIN OPENSSH PRIVATE KEY-----\n",
+                 "-----END OPENSSH PRIVATE KEY-----\n",
+                 fun openssh_key_v1_encode/1}
+        end,
+    iolist_to_binary(
+      [
+       [Begin,
+        [rfc4716_encode_header(H) || H <- proplists:get_value(headers, Attrs, [])],
+        split_long_lines( base64:encode( F(Key) ) ),
+        "\n",
+        End
+       ] ||
+          {Key,Attrs} <- KeyAttrs
+      ]
+     );
+
+encode(KeyAttrs, Type) when Type == known_hosts;
+                            Type == auth_keys ;
+                            Type == openssh_key ->
+    FirstArgTag =
+        case Type of
+            known_hosts -> hostnames;
+            auth_keys -> options;
+            openssh_key -> '*no tag*'
+        end,
+    iolist_to_binary(
+      [
+       begin
+           <<?DEC_BIN(KeyType,__0),_/binary>> = Enc = ssh_message:ssh2_pubkey_encode(Key),
+           [case lists:join(",", proplists:get_value(FirstArgTag, Attributes, [])) of
+                [] -> "";
+                C -> [C," "]
+            end,
+            KeyType, " ",
+            base64:encode(Enc), " ",
+            case proplists:get_value(comment, Attributes, []) of
+                [] -> "";
+                C -> C
+            end,
+            "\n"
+           ]
+       end
+       || {Key,Attributes} <- KeyAttrs
+      ]
+     );
+
+encode(_KeyBin, _Type) ->
+    error(badarg).
+
+%%%----------------------------------------------------------------
+
+-spec extract_public_key(PrivKey) -> PubKey
+                        when PrivKey :: public_key:private_key(),
+                              PubKey :: public_key:public_key().
+
+extract_public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
+    #'RSAPublicKey'{modulus = N, publicExponent = E};
+extract_public_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
+    {Y,  #'Dss-Parms'{p=P, q=Q, g=G}};
+extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
+				   publicKey = Pub0, privateKey = Priv}) when
+      OID == ?'id-Ed25519' orelse
+      OID == ?'id-Ed448' ->
+    case {pubkey_cert_records:namedCurves(OID), Pub0} of
+        {Alg, asn1_NOVALUE} ->
+            %% If we're missing the public key, we can create it with
+            %% the private key.
+            {Pub, Priv} = crypto:generate_key(eddsa, Alg, Priv),
+            {#'ECPoint'{point=Pub}, {namedCurve,OID}};
+        {_Alg, Pub} ->
+            {#'ECPoint'{point=Pub}, {namedCurve,OID}}
+    end;
+extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
+				   publicKey = Q}) when is_tuple(OID) ->
+    {#'ECPoint'{point=Q}, {namedCurve,OID}};
+extract_public_key(#{engine:=_, key_id:=_, algorithm:=Alg} = M) ->
+    case {Alg, crypto:privkey_to_pubkey(Alg, M)} of
+        {rsa, [E,N]} ->
+            #'RSAPublicKey'{modulus = N, publicExponent = E};
+        {dss, [P,Q,G,Y]} ->
+            {Y, #'Dss-Parms'{p=P, q=Q, g=G}}
+    end.
+
 %%%================================================================
 %%%
 %%% Local functions
@@ -158,7 +430,7 @@ lookup_auth_keys(KeyType, Key, File, Opt
         time ->
             case file:read_file(File) of
                 {ok,Bin} ->
-                    Lines = binary:split(Bin, <<"\n">>, [global,trim_all]),
+                    Lines = split_in_lines(Bin),
                     find_key(KeyType, Key, Lines);
                 _ ->
                     false
@@ -174,7 +446,7 @@ lookup_auth_keys(KeyType, Key, File, Opt
                     file:close(Fd),
                     Result;
                 {error,_Error} ->
-                    false
+                   false
             end;
         Other ->
             {error,{is_auth_key,{opt,Other}}}
@@ -215,7 +487,7 @@ normalize_hosts_list(Hosts, Port) when i
                           Hs = case Port of
                                    22 -> H1s;
                                    _ -> [lists:concat(["[",Hx,"]:",Port]) || Hx <- H1s]
-                              end,
+                               end,
                           lists:foldl(
                             fun(Hy, Acc2) ->
                                     case lists:member(Hy, Acc2) of
@@ -270,7 +542,7 @@ read_test_loop(Fd, Test) ->
 	    %% Rare... For example NFS errors
 	    {error,Error};
 	Line0 ->
-            case binary:split(Line0, <<"\n">>, [global,trim_all]) of % remove trailing \n
+            case split_in_lines(Line0) of % remove trailing EOL
                 [Line] ->
                     case Test(Line) of
                         false ->
@@ -282,7 +554,7 @@ read_test_loop(Fd, Test) ->
                     read_test_loop(Fd, Test)
             end
     end.
-                    
+
 %%%--------------------------------
 
 lookup_host_keys(Hosts, KeyType, Key, File, Opts) ->
@@ -290,7 +562,7 @@ lookup_host_keys(Hosts, KeyType, Key, Fi
         time ->
             case file:read_file(File) of
                 {ok,Bin} ->
-                    Lines = binary:split(Bin, <<"\n">>, [global,trim_all]),
+                    Lines = split_in_lines(Bin),
                     case find_host_key(Hosts, KeyType, Key, Lines) of
                         {true,RestLines} ->
                             case revoked_key(Hosts, KeyType, Key, RestLines) of
@@ -364,7 +636,7 @@ find_host_key(_, _, _, []) ->
 revoked_key(Hosts, KeyType, EncKey, [<<"@revoked ",RestLine/binary>> | Lines]) ->
     case binary:split(RestLine, <<" ">>, [global,trim_all]) of
         [Patterns, KeyType, EncKey|_Comment] ->
-            %% Very likeley to be a revoked key,
+            %% Very likely to be a revoked key,
             %% but does any of the hosts match the pattern?
             case host_match(Hosts, Patterns) of
                 true ->
@@ -411,7 +683,7 @@ line_match(_, _, _, _) ->
 host_match(Hosts, Patterns) ->
     PatternList = binary:split(Patterns, <<",">>, [global]),
     host_matchL(Hosts, PatternList).
-    
+
 host_matchL([H|Hosts], Patterns) ->
     case one_host_match(H, Patterns) of
         true ->
@@ -458,18 +730,45 @@ pos_match(H, P) ->
 
         {[Hh], [Ph,<<"*">>]} ->
             %% host [host]:*
-            Sz = size(Hh),
+            Sz = byte_size(Hh),
             Ph == <<"[", Hh:Sz/binary, "]">>;
 
         {[Hh], [Ph,<<"22">>]} ->
             %% host [host]:22
-            Sz = size(Hh),
+            Sz = byte_size(Hh),
             Ph == <<"[", Hh:Sz/binary, "]">>;
 
         _ ->
             false
     end.
 
+%%%---------------- UTILITY ---------------------------------------
+rfc4716_encode_header({Tag, Value}) ->
+    TagLen = length(Tag),
+    ValueLen = length(Value),
+    case TagLen + 1 + ValueLen of
+	N when N > ?ENCODED_LINE_LENGTH ->
+	    NumOfChars =  ?ENCODED_LINE_LENGTH - (TagLen + 1),
+	    {First, Rest} = lists:split(NumOfChars, Value),
+	    [Tag,": " , First, [$\\], "\n", rfc4716_encode_value(Rest) , "\n"];
+	_ ->
+	    [Tag, ": ", Value, "\n"]
+    end.
+
+rfc4716_encode_value(Value) ->
+    case length(Value) of
+	N when N > ?ENCODED_LINE_LENGTH ->
+	    {First, Rest} = lists:split(?ENCODED_LINE_LENGTH, Value),
+	    [First, [$\\], "\n", rfc4716_encode_value(Rest)];
+	_ ->
+	    Value
+    end.
+
+split_long_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) when Rest =/= <<"">> ->
+    [Text, $\n | split_long_lines(Rest)];
+split_long_lines(Bin) ->
+    [Bin].
+
 %%%---------------- COMMON FUNCTIONS ------------------------------
 
 assure_file_mode(File, user_write) -> assure_file_mode(File, 8#200);
@@ -515,7 +814,7 @@ read_ssh_key_file(Role, PrivPub, Algorit
             try
                 decode_ssh_file(PrivPub, Algorithm, Pem, Password)
             of
-                {ok, [Key|_Keys]} ->
+                {ok, [{Key,_Attrs}|_Keys]} ->
                     {ok,Key};
                 {error, Reason} ->
                     {error, Reason}
@@ -533,19 +832,23 @@ read_ssh_key_file(Role, PrivPub, Algorit
 
 -spec decode_ssh_file(PrivPub, Algorithm, Pem, Password) -> Result when
       PrivPub :: private | public,
-      Algorithm :: ssh:pubkey_alg(),
+      Algorithm :: ssh:pubkey_alg() | any,
       Pem :: binary(),
-      Password :: string(),
+      Password :: string() | ignore,
       Result :: {ok, Keys} | {error, any()},
-      Keys :: [Key],
+      Keys :: [{Key,Attrs}],
+      Attrs :: [{any(),any()}],
       Key :: public_key:private_key() | public_key:public_key() .
-                             
+
 decode_ssh_file(PrivPub, Algorithm, Pem, Password) ->
     try decode_pem_keys(Pem, Password)
     of
+        {ok, Keys} when Algorithm == any ->
+            {ok, Keys};
+
         {ok, Keys0} ->
-            case [Key || Key <- Keys0,
-                         ssh_transport:valid_key_sha_alg(PrivPub, Key, Algorithm)] of
+            case [{Key,Attrs} || {Key,Attrs} <- Keys0,
+                                 ssh_transport:valid_key_sha_alg(PrivPub, Key, Algorithm)] of
                 [] ->
                     {error,no_key_found};
                 Keys ->
@@ -560,26 +863,40 @@ decode_ssh_file(PrivPub, Algorithm, Pem,
             {error, key_decode_failed}
     end.
 
-decode_pem_keys(Pem, Password) ->
+
+decode_pem_keys(RawBin, Password) ->
+    PemLines = split_in_lines(
+                 binary:replace(RawBin, [<<"\\\n">>,<<"\\\r\\\n">>],  <<"">>, [global])
+                ),
+    decode_pem_keys(PemLines, Password, []).
+decode_pem_keys([], _, Acc) ->
+    {ok,lists:reverse(Acc)};
+
+
+decode_pem_keys(PemLines, Password, Acc) ->
     %% Private Key
-    try get_key_part(Pem) of
-        {'openssh-key-v1', Bin, _KeyValues} ->
+    try get_key_part(PemLines) of
+        {'openssh-key-v1', Bin, Attrs, RestLines} ->
+            %% -----BEGIN OPENSSH PRIVATE KEY-----
             %% Holds both public and private keys
-            KeyPairs = new_openssh_decode(Bin, Password),
-            Keys = [Key || {Pub,Priv} <- KeyPairs,
-                           Key <- [Pub,Priv]],
-            {ok,Keys};
+            KeyPairs = openssh_key_v1_decode(Bin, Password),
+            Keys = [{Key,Attrs} || {Pub,Priv} <- KeyPairs,
+                                   Key <- [Pub,Priv]],
+            decode_pem_keys(RestLines, Password, Keys ++ Acc);
 
-        {rfc4716, Bin, _KeyValues} ->
+        {rfc4716, Bin, Attrs, RestLines} ->
+            %% ---- BEGIN SSH2 PUBLIC KEY ----
             %% rfc4716 only defines public keys
             Key = ssh_message:ssh2_pubkey_decode(Bin),
-            {ok,[Key]};
+            decode_pem_keys(RestLines, Password, [{Key,Attrs}|Acc]);
 
-        {Type, Bin, KeyValues} ->
-            case get_encrypt_hdrs(KeyValues) of
+        {Type, Bin, Attrs, RestLines} ->
+            %% -----BEGIN (RSA|DSA|EC) PRIVATE KEY-----
+            %% and possibly others
+            case get_encrypt_hdrs(Attrs) of
                 not_encrypted ->
                     Key = public_key:pem_entry_decode({Type,Bin,not_encrypted}),
-                    {ok, [Key]};
+                    decode_pem_keys(RestLines, Password, [{Key,Attrs}|Acc]);
 
                 [Cipher,Salt] when is_binary(Cipher),
                                    is_binary(Salt),
@@ -587,7 +904,7 @@ decode_pem_keys(Pem, Password) ->
                     CryptInfo =
                         {binary_to_list(Cipher), unhex(binary_to_list(Salt))},
                     Key = public_key:pem_entry_decode({Type,Bin,CryptInfo}, Password),
-                    {ok, [Key]};
+                    decode_pem_keys(RestLines, Password, [{Key,Attrs}|Acc]);
 
                 _X ->
                     {error, no_pass_phrase}
@@ -696,25 +1013,21 @@ default_user_dir(Home) when is_list(Home
     UserDir.
 
 %%%################################################################
-get_key_part(RawBin) when is_binary(RawBin) ->
-    case binary:split(
-           binary:replace(RawBin, <<"\\\n">>, <<"">>, [global]),
-           <<"\n">>, [global,trim_all])
-    of
-        [<<"---- BEGIN SSH2 PUBLIC KEY ----">> | Lines0] ->
-            %% RFC 4716 format
-            {KeyValues,Lines} = get_hdr_lines(Lines0, []),
-            ExpectedEndLine = <<"---- END SSH2 PUBLIC KEY ----">>,
-            {rfc4716, get_body(Lines,ExpectedEndLine), KeyValues};
-
-        [<<"-----BEGIN ", Rest/binary>> | Lines0] ->
-            %% PEM format
-            ExpectedEndLine = <<"-----END ",Rest/binary>>,
-            [MiddlePart, <<>>] = binary:split(Rest,  <<" KEY-----">>),
-            {KeyValues,Lines} = get_hdr_lines(Lines0, []),
-            {asn1_type(MiddlePart), get_body(Lines,ExpectedEndLine), KeyValues}
-    end.
-            
+get_key_part([<<"---- BEGIN SSH2 PUBLIC KEY ----">> | Lines0]) ->
+    %% RFC 4716 format
+    {KeyValues,Lines} = get_hdr_lines(Lines0, []),
+    ExpectedEndLine = <<"---- END SSH2 PUBLIC KEY ----">>,
+    {Key,RestLines} = get_body(Lines,ExpectedEndLine),
+    {rfc4716, Key, KeyValues, RestLines};
+
+get_key_part([<<"-----BEGIN ", Rest/binary>> | Lines0]) ->
+    %% PEM format
+    ExpectedEndLine = <<"-----END ",Rest/binary>>,
+    [MiddlePart, <<>>] = binary:split(Rest,  <<" KEY-----">>),
+    {KeyValues,Lines} = get_hdr_lines(Lines0, []),
+    {Key,RestLines} = get_body(Lines,ExpectedEndLine),
+    {asn1_type(MiddlePart), Key, KeyValues, RestLines}.
+
 
 get_hdr_lines(Lines, Acc) ->
     Line1 = hd(Lines),
@@ -727,8 +1040,9 @@ get_hdr_lines(Lines, Acc) ->
 
 
 get_body(Lines, ExpectedEndLine) ->
-    {KeyPart, [ExpectedEndLine]} = lists:split(length(Lines)-1, Lines),
-    base64:mime_decode(iolist_to_binary(KeyPart)).
+    {KeyPart, [ExpectedEndLine|RestLines]} = 
+        lists:splitwith(fun(L) -> L=/=ExpectedEndLine end, Lines),
+    {base64:mime_decode(iolist_to_binary(KeyPart)), RestLines}.
 
 trim(<<" ",B/binary>>) -> trim(B);
 trim(B) -> B.
@@ -738,6 +1052,7 @@ asn1_type(<<"RSA PUBLIC">>) -> 'RSAPubli
 asn1_type(<<"DSA PRIVATE">>) -> 'DSAPrivateKey';
 asn1_type(<<"EC PRIVATE">>) -> 'ECPrivateKey';
 asn1_type(<<"OPENSSH PRIVATE">>) -> 'openssh-key-v1';
+asn1_type(<<"PRIVATE">>) -> 'PrivateKeyInfo';
 asn1_type(_) -> undefined.
 
 %%%================================================================
@@ -746,55 +1061,52 @@ asn1_type(_) -> undefined.
 
 -define(NON_CRYPT_BLOCKSIZE, 8).
 
-new_openssh_decode(<<"openssh-key-v1",0,
-                     ?DEC_BIN(CipherName, _L1),
-                     ?DEC_BIN(KdfName, _L2),
-                     ?DEC_BIN(KdfOptions, _L3),
-                     ?UINT32(N),      % number of keys
-                     Rest/binary
-                   >>, Pwd) ->
-    new_openssh_decode(Rest, N, Pwd, CipherName, KdfName, KdfOptions, N, []).
+openssh_key_v1_decode(<<"openssh-key-v1",0,
+                        ?DEC_BIN(CipherName, _L1),
+                        ?DEC_BIN(KdfName, _L2),
+                        ?DEC_BIN(KdfOptions, _L3),
+                        ?UINT32(N),      % number of keys
+                        Rest/binary
+                      >>, Pwd) ->
+    openssh_key_v1_decode(Rest, N, Pwd, CipherName, KdfName, KdfOptions, N, []).
 
 
-new_openssh_decode(<<?DEC_BIN(BinKey,_L1), Rest/binary>>, I, Pwd, CipherName, KdfName, KdfOptions, N, PubKeyAcc) when I>0 ->
+openssh_key_v1_decode(<<?DEC_BIN(BinKey,_L1), Rest/binary>>, I,
+                      Pwd, CipherName, KdfName, KdfOptions, N, PubKeyAcc) when I>0 ->
     PublicKey = ssh_message:ssh2_pubkey_decode(BinKey),
-    new_openssh_decode(Rest, I-1, Pwd, CipherName, KdfName, KdfOptions, N, [PublicKey|PubKeyAcc]);
+    openssh_key_v1_decode(Rest, I-1, Pwd, CipherName, KdfName, KdfOptions, N, [PublicKey|PubKeyAcc]);
 
-new_openssh_decode(<<?DEC_BIN(Encrypted,_L)>>,
-                   0, Pwd, CipherName, KdfName, KdfOptions, N, PubKeyAccRev) ->
+openssh_key_v1_decode(<<?DEC_BIN(Encrypted,_L)>>,
+                      0, Pwd, CipherName, KdfName, KdfOptions, N, PubKeyAccRev) ->
     PubKeys = lists:reverse(PubKeyAccRev),
     try
-        Plain = decrypt_new_openssh(Encrypted, KdfName, KdfOptions, CipherName, Pwd),
-        new_openssh_decode_priv_keys(Plain, N, N, [], [])
+        Plain = decrypt_openssh_key_v1(Encrypted, KdfName, KdfOptions, CipherName, Pwd),
+        openssh_key_v1_decode_priv_keys(Plain, N, N, [], [])
     of
         {PrivKeys, _Comments} ->
-            lists:map(fun({ {ed_pub,A,Pub}, {ed_pri,A,Pub,Pri0} }) ->
-                              Pri = binary:part(Pri0, {0,size(Pri0)-size(Pub)}),
-                              {{ed_pub,A,Pub}, {ed_pri,A,Pub,Pri}};
-                         (Pair) ->
-                              Pair
-                      end, lists:zip(PubKeys, PrivKeys))
+            lists:zip(PubKeys, PrivKeys)
+            %% lists:zip3(PubKeys, PrivKeys,_ Comments))
     catch
         error:{decryption, DecryptError} ->
             error({decryption, DecryptError})
     end.
 
 
-new_openssh_decode_priv_keys(Bin, I, N, KeyAcc, CmntAcc) when I>0 ->
+openssh_key_v1_decode_priv_keys(Bin, I, N, KeyAcc, CmntAcc) when I>0 ->
     {PrivKey, <<?DEC_BIN(Comment,_Lc),Rest/binary>>} = ssh_message:ssh2_privkey_decode2(Bin),
-    new_openssh_decode_priv_keys(Rest, I-1, N, [PrivKey|KeyAcc], [Comment|CmntAcc]);
-new_openssh_decode_priv_keys(_Padding, 0, _N, PrivKeyAccRev, CommentAccRev) ->
+    openssh_key_v1_decode_priv_keys(Rest, I-1, N, [PrivKey|KeyAcc], [Comment|CmntAcc]);
+openssh_key_v1_decode_priv_keys(_Padding, 0, _N, PrivKeyAccRev, CommentAccRev) ->
     {lists:reverse(PrivKeyAccRev),
      lists:reverse(CommentAccRev)}.
 
 
-decrypt_new_openssh(Encrypted, <<"none">>, <<>>, _CipherName, _Pwd) ->
+decrypt_openssh_key_v1(Encrypted, <<"none">>, <<>>, _CipherName, _Pwd) ->
     check_valid_decryption(Encrypted, ?NON_CRYPT_BLOCKSIZE);
-decrypt_new_openssh(Encrypted, <<>>, <<>>, _CipherName, _Pwd) ->
+decrypt_openssh_key_v1(Encrypted, <<>>, <<>>, _CipherName, _Pwd) ->
     check_valid_decryption(Encrypted, ?NON_CRYPT_BLOCKSIZE);
-decrypt_new_openssh(_Encrypted, <<"bcrypt">>, <<?DEC_BIN(_Salt,_L),?UINT32(_Rounds)>>, _CipherName, _Pwd) ->
+decrypt_openssh_key_v1(_Encrypted, <<"bcrypt">>, <<?DEC_BIN(_Salt,_L),?UINT32(_Rounds)>>, _CipherName, _Pwd) ->
     error({decryption, {not_supported,bcrypt}});
-decrypt_new_openssh(_Encrypted, KdfName, _KdfOpts, _CipherName, _Pwd) ->
+decrypt_openssh_key_v1(_Encrypted, KdfName, _KdfOpts, _CipherName, _Pwd) ->
     error({decryption, {not_supported,KdfName}}).
 
 
@@ -821,5 +1133,78 @@ check_padding(Bin, BlockSize) ->
             true
     end.
 
+%%%----------------------------------------------------------------
+%% KeyPairs :: [ {Pub,Priv,Comment} ]
+openssh_key_v1_encode(KeyPairs) ->
+    CipherName = <<"none">>,
+    BlockSize = ?NON_CRYPT_BLOCKSIZE, % Cipher dependent
+    KdfName = <<"none">>,
+    KdfOptions = <<>>,
+    NumKeys = length(KeyPairs),
+    CheckInt = crypto:strong_rand_bytes(4),
+    UnEncrypted0 = <<CheckInt/binary,
+                     CheckInt/binary,
+                     (openssh_key_v1_encode_priv_keys_cmnts(KeyPairs))/binary>>,
+    UnEncrypted = <<UnEncrypted0/binary,
+                    (pad(byte_size(UnEncrypted0), BlockSize))/binary>>,
+    Encrypted = encrypt_openssh_key_v1(UnEncrypted,  KdfName, KdfOptions, CipherName, ignore),
+    <<"openssh-key-v1",0,
+      ?STRING(CipherName),
+      ?STRING(KdfName),
+      ?STRING(KdfOptions),
+      ?UINT32(NumKeys),
+      (openssh_key_v1_encode_pub_keys(KeyPairs))/binary,
+      ?STRING(Encrypted)>>.
+
+%%%----
+openssh_key_v1_encode_pub_keys(KeyPairs) ->
+    openssh_key_v1_encode_pub_keys(KeyPairs, []).
+
+openssh_key_v1_encode_pub_keys([{Priv = #'ECPrivateKey'{}, _Cmnt} | Ks], Acc) ->
+    Pub = extract_public_key(Priv),
+    Bk = ssh_message:ssh2_pubkey_encode(Pub),
+    openssh_key_v1_encode_pub_keys(Ks, [<<?STRING(Bk)>>|Acc]);
+openssh_key_v1_encode_pub_keys([{K,_,_C}|Ks], Acc) ->
+    Bk = ssh_message:ssh2_pubkey_encode(K),
+    openssh_key_v1_encode_pub_keys(Ks, [<<?STRING(Bk)>>|Acc]);
+openssh_key_v1_encode_pub_keys([], Acc) ->
+    list_to_binary(lists:reverse(Acc)).
+
+
+%%%----
+openssh_key_v1_encode_priv_keys_cmnts(KeyPairs) ->
+    openssh_key_v1_encode_priv_keys_cmnts(KeyPairs, []).
+
+openssh_key_v1_encode_priv_keys_cmnts([{K = #'ECPrivateKey'{}, C} | Ks], Acc) ->
+    Bk = ssh_message:ssh2_privkey_encode(K),
+    openssh_key_v1_encode_priv_keys_cmnts(Ks, [<<Bk/binary,?STRING(C)>>|Acc]);
+openssh_key_v1_encode_priv_keys_cmnts([{_,K,C}|Ks], Acc) ->
+    Bk = ssh_message:ssh2_privkey_encode(K),
+    openssh_key_v1_encode_priv_keys_cmnts(Ks, [<<Bk/binary,?STRING(C)>>|Acc]);
+openssh_key_v1_encode_priv_keys_cmnts([], Acc) ->
+    list_to_binary(lists:reverse(Acc)).
+
+encrypt_openssh_key_v1(UnEncrypted, <<"none">>, <<>>, _CipherName, _Pwd) ->
+    UnEncrypted;
+encrypt_openssh_key_v1(_UnEncrypted,  KdfName, _KdfOptions, _CipherName, _Pwd) ->
+    error({decryption, {not_supported,KdfName}}).
+
+pad(N, BlockSize) when N>BlockSize -> pad(N rem BlockSize, BlockSize);
+pad(N, BlockSize) -> list_to_binary(lists:seq(1,BlockSize-N)).
+
 %%%================================================================
 %%%
+split_in_nonempty_lines(Bin) ->
+    skip_blank_lines_and_comments( split_in_lines(Bin) ).
+
+split_in_lines(Bin) ->
+    binary:split(Bin, [<<"\n">>,<<"\r\n">>], [global,trim_all]).
+
+skip_blank_lines_and_comments(Lines) ->
+    lists:filter(fun(<<"#",_/binary>>) ->
+                         %% skip comments
+                         false;
+                    (L) ->
+                         %% skip blank lines
+                         re:run(L, "^(\t|\s)+$") == nomatch
+                 end, Lines).
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_io.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_io.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_io.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -27,49 +27,31 @@
 -export([yes_no/2, read_password/2, read_line/2, format/2]).
 -include("ssh.hrl").
 
-read_line(Prompt, Opts) ->
+read_line(Prompt, _Opts) ->
     format("~s", [listify(Prompt)]),
-    ?GET_INTERNAL_OPT(user_pid, Opts) ! {self(), question},
-    receive
-	Answer when is_list(Answer) or is_binary(Answer) ->
-	    unicode:characters_to_list(Answer)
-    end.
+    unicode:characters_to_list(io:get_line("")).
 
 yes_no(Prompt, Opts) ->
     format("~s [y/n]?", [Prompt]),
-    ?GET_INTERNAL_OPT(user_pid, Opts) ! {self(), question},
-    receive
-	%% I can't see that the atoms y and n are ever received, but it must
-	%% be investigated before removing
-	y -> yes;
-	n -> no;
-
-	Answer when is_list(Answer) or is_binary(Answer) ->
-	    case trim(Answer) of
-		"y" -> yes;
-		"n" -> no;
-		"Y" -> yes;
-		"N" -> no;
-		_ ->
-		    format("please answer y or n\n",[]),
-		    yes_no(Prompt, Opts)
-	    end
+    case trim(io:get_line("")) of
+        "y" -> yes;
+        "n" -> no;
+        "Y" -> yes;
+        "N" -> no;
+        _ ->
+            format("please answer y or n\n",[]),
+            yes_no(Prompt, Opts)
     end.
 
 read_password(Prompt, Opts) ->
     format("~s", [listify(Prompt)]),
-    ?GET_INTERNAL_OPT(user_pid, Opts) ! {self(), user_password},
-    receive
-	Answer when is_list(Answer) or is_binary(Answer) ->
-	     case trim(Answer) of
-		 "" ->
-		     read_password(Prompt, Opts);
-		 Pwd ->
-		     Pwd
-	     end
+    case trim(io:get_password()) of
+        "" ->
+            read_password(Prompt, Opts);
+        Pwd ->
+            Pwd
     end.
 
-
 format(Fmt, Args) ->
     io:format(Fmt, Args).
 
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_server_channel.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_server_channel.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_server_channel.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2013-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@
 
 -module(ssh_server_channel).
 
-%% API to server side channel that can be pluged into the erlang ssh daemeon
+%% API to server side channel that can be plugged into the erlang ssh daemeon
 -callback init(Args :: term()) ->
     {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} |
     {stop, Reason :: term()} | ignore.
@@ -44,7 +44,7 @@
 
 %%% Internal API
 -export([start_link/5,
-         get_print_info/1
+         get_print_info/1, get_print_info/2
         ]).
 
 start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) ->
@@ -53,3 +53,6 @@ start_link(ConnectionManager, ChannelId,
 
 get_print_info(Pid) ->
     ssh_client_channel:get_print_info(Pid).
+
+get_print_info(Pid, Arg) ->
+    ssh_client_channel:get_print_info(Pid, Arg).
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_server_key_api.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_server_key_api.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_server_key_api.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2011-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
 %%% The option key_cb_private is to pass options needed by other
 %%% callback modules than the default ssh_file.erl
 %%%
-%%% If ssh:deamon(n, [ {key_cb_private, {hi,{there}}} ]
+%%% If ssh:daemon(n, [ {key_cb_private, {hi,{there}}} ]
 %%% is called, the term() will be {hi,{there}}
 
 -type daemon_key_cb_options(T) :: [{key_cb_private,[T]} | ssh:daemon_option()].
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_shell.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_shell.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_shell.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2009-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2022. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
 -include("ssh_connect.hrl").
 
 %%% As this is an user interactive client it behaves like a daemon
-%%% channel inspite of it being a client. 
+%%% channel in spite of it being a client. 
 -behaviour(ssh_server_channel).
 
 %% ssh_server_channel callbacks
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_tcpip_forward_acceptor_sup.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_tcpip_forward_acceptor_sup.erl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_tcpip_forward_acceptor_sup.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -41,20 +41,21 @@ start_link() ->
 
 start_child(Sup, LSock, ListenAddr, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
     Args = [LSock, ListenAddr, ConnectToAddr, ChanType, ChanCB, ConnPid],
-    supervisor:start_child(Sup, Args).
+    supervisor:start_child(Sup, 
+                           #{id     => {ListenAddr,ConnectToAddr},
+                             start  => {ssh_tcpip_forward_acceptor, start_link, Args}
+                            }).
+    
 
 %%%=========================================================================
 %%%  Supervisor callback
 %%%=========================================================================
 init([]) ->
-    SupFlags = #{strategy  => simple_one_for_one, 
+    SupFlags = #{strategy  => one_for_one, 
                  intensity =>   10,
                  period    => 3600
                 },
-    ChildSpecs = [#{id     => undefined, % As simple_one_for_one is used.
-                    start  => {ssh_tcpip_forward_acceptor, start_link, []}
-                   }
-                 ],
+    ChildSpecs = [],
     {ok, {SupFlags,ChildSpecs}}.
 
 %%%=========================================================================
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_transport.hrl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_transport.hrl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_transport.hrl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
 
 %%
 %%----------------------------------------------------------------------
-%% Purpose: Record and constant defenitions for the SSH-tansport layer 
+%% Purpose: Record and constant definitions for the SSH-tansport layer 
 %% protocol see RFC 4253
 %%----------------------------------------------------------------------
 
@@ -266,5 +266,7 @@
 -define(dh_group18,
         {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF}).
 
-
+%%% OpenSSH KEX strict
+-define(kex_strict_c, "kex-strict-c-v00@openssh.com").
+-define(kex_strict_s, "kex-strict-s-v00@openssh.com").
 -endif. % -ifdef(ssh_transport).
Index: otp-OTP-23.3.4.19/lib/ssh/src/ssh_xfer.hrl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/src/ssh_xfer.hrl
+++ otp-OTP-23.3.4.19/lib/ssh/src/ssh_xfer.hrl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2022. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -226,7 +226,7 @@
 
 -record(ssh_xfer_attr,
 	{
-	  type,    %% regular, dirctory, symlink, ...
+	  type,    %% regular, directory, symlink, ...
 	  size,
 	  owner,
 	  group,
Index: otp-OTP-23.3.4.19/lib/ssh/test/property_test/ssh_eqc_client_server.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/property_test/ssh_eqc_client_server.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/property_test/ssh_eqc_client_server.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %% 
-%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2022. All Rights Reserved.
 %% 
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -299,7 +299,7 @@ do(Pid, Fun, Timeout) when is_function(F
 	    
 %%%----------------
 %%% Start a new connection
-%%% Precondition:  deamon exists
+%%% Precondition:  daemon exists
 
 ssh_open_connection_pre(S) -> S#state.servers /= [].
     
Index: otp-OTP-23.3.4.19/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %% 
-%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2022. All Rights Reserved.
 %% 
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -250,7 +250,7 @@ msg_code(Num) -> Name
 -include_lib("ssh/src/ssh_connect.hrl").
 -include_lib("ssh/src/ssh_transport.hrl").
 
-%%% Encoding and decodeing is asymetric so out=binary in=string. Sometimes. :(
+%%% Encoding and decoding is asymmetric so out=binary in=string. Sometimes. :(
 -define(fix_asym_Xdh_reply(S),
  fix_asym(#S{public_host_key = Key, h_sig = {Alg,Sig}} = M) ->
       M#S{public_host_key = {Key, list_to_atom(Alg)}, h_sig = Sig}
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed25519_key
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed25519_key
@@ -0,0 +1,4 @@
+-----BEGIN EC PRIVATE KEY-----
+MFECAQEEIFZTDpCko9CxtWW5UKzRqvMXPTJZfIdcA5u/IOV3EmU4oAUGAytlcKEj
+AyEAIAJ1D3OUNmUJRCDQ8uPkVCeTV6oNOtpubn2I7x2VxM0=
+-----END EC PRIVATE KEY-----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed25519_key.pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed25519_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICACdQ9zlDZlCUQg0PLj5FQnk1eqDTrabm59iO8dlcTN uabhnil@elxadlj3q32
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed448_key
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed448_key
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGDAgEBBDlf6cdZBOKnN7msVY8oloq9Se2ee5RBXGf3Ofs96djk8uqEAyQdWUe+
+fWMNDPVnyViy7qceZkECkf6gBQYDK2VxoTwDOgCLHixRiwKzXM1/OOTdsC1L06OS
+3BntXrBWpqKU2Xhr1acxidyCB/0hhtf89NtZVm0zkXBf/zL6jQA=
+-----END EC PRIVATE KEY-----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed448_key.pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format/ssh_host_ed448_key.pub
@@ -0,0 +1 @@
+ssh-ed448 AAAACXNzaC1lZDQ0OAAAADmLHixRiwKzXM1/OOTdsC1L06OS3BntXrBWpqKU2Xhr1acxidyCB/0hhtf89NtZVm0zkXBf/zL6jQA= uabhnil@elxadlj3q32
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed25519_key
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed25519_key
@@ -0,0 +1,4 @@
+-----BEGIN EC PRIVATE KEY-----
+MFECAQEEIFZTDpCko9CxtWW5UKzRqvMXPTJZfIdcA5u/IOV3EmU4oAUGAytlcKEj
+AyEAIAJ1D3OUNmUJRCDQ8uPkVCeTV6oNOtpubn2I7x2VxM0=
+-----END EC PRIVATE KEY-----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed25519_key.pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed25519_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICACdQ9zlDZlCUQg0PLj5FQnk1eqDTrabm59iO8dlcTN uabhnil@elxadlj3q32
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed448_key
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed448_key
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGDAgEBBDlf6cdZBOKnN7msVY8oloq9Se2ee5RBXGf3Ofs96djk8uqEAyQdWUe+
+fWMNDPVnyViy7qceZkECkf6gBQYDK2VxoTwDOgCLHixRiwKzXM1/OOTdsC1L06OS
+3BntXrBWpqKU2Xhr1acxidyCB/0hhtf89NtZVm0zkXBf/zL6jQA=
+-----END EC PRIVATE KEY-----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed448_key.pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/old_format_passphrase/ssh_host_ed448_key.pub
@@ -0,0 +1 @@
+ssh-ed448 AAAACXNzaC1lZDQ0OAAAADmLHixRiwKzXM1/OOTdsC1L06OS3BntXrBWpqKU2Xhr1acxidyCB/0hhtf89NtZVm0zkXBf/zL6jQA= uabhnil@elxadlj3q32
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/id_rsa
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/id_rsa
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2d3XMIA8GTEQc
+UFCOm31M5jt6lGjN61ZYGnXBVBjEcyJynB7Y3C437cDpjmvbSWF1oSVVDTwMERwn
+XzixLG//7w8K7i6aJLKpHKtS91qnrQidmrUWDnQ4kx8AZxaN46nhSsf+cZ0nKp03
+ZjjR5WxeDimiDLsSUbdDmFE6ZsL2+k5OStvcqu/skUVfPe+FGTGJgIw3DyErxM7J
+72jUkLJXMiZkYbB1QD05k3g2LOiPqJ73QoJVGgj7YagTSA3Lgy3s/6U7IMHMV4ls
+aXShv1Lk/eCfIJVSaVXQRjV9KKM3wgg6PmWqwGkAO36w3eJiW1kmYKfnAM/+I5Gf
+o/TiNZTXAgMBAAECggEAeWdwfDmUZZdW9hPGFayFKSZCyuN1/BSqZYJteQ2QUR1d
+/S29JIMTSWkqovt87fGcI9ztbvKYUlsMBXJI0TGE75/KvXYOkcb7DKQjpdcofUoW
+4m4uMJe7Ym0ZAnaUviGNRXYxLS3A529mHZcpFRb2DHqV3tljmuO98P6mhRocfKCc
+3p/T4+LIGlRlov5lOP/oKkeILF1m04J/SjptTtNo81xne9/dCGTiOTXjS8QMslCc
+8Xyy0Go9Zr0d+YzsI2NYF2aFBce0fDwK0Xpptr0FEL8UxjHjeK0T2GSDqncmtKoA
+3+BnpEJcuiDqZBi20lX7LygtNe9uVPZjdz1iOeKAaQKBgQDZjj6yOwbDeXkedD0I
+25RC8lmCWhV381PDz9RdeRXVC50jq3OYwmdcDEIK23YNWU8GoUnvi7B1aljSfAUm
+yUSnixXpU+/ZOkGYA48MHpC1DxJeEVZDu+MFWHmTCXctQNUj40gAKhozJm5Lo33s
+Wnhr+Yq2CP65w6R+vXn4gXsv9QKBgQDWtd0BMEVCUug+6/dWCVTUsuBouz/erOgE
+f0PPA8/IQV1ZhBQK4wewv14R8Nkywb8Z5lsVyH8JHRHZC35mVzFxGJyGAzDfJ3Mg
+GoK8t7jjiUHPF0tYWpLmAKdKHmJqB7ZBGzT7pAP07XockRHoeYHBHoO3Ck/c3h0f
+EtclGMOuGwKBgQCXf8z9RMmS+lZz9LJEJtT6QdY/RghJPbOJWoMijJ29fJbzLgQT
+zt03ZnnfIbD13sl/bnYUUIyTV3l/KkpUFjivC9Y4Y/FUrpLbDy9gWzCeRV6fDyep
+h3+yS0huMltBsjI7CZ0sMCWKlSqdlb6tBttxJZeI6H6qUimM8NmtSk3EuQKBgAw+
+OIjt0LU0dwvHdsYQKCcswAEY1E6FO4GuJBa01+9KUuFc16u7QGACuYF6Y1gylgwL
+B5yZXy0M3EytDBsX07joN1yo5+uBm130RQovy7olxHvjjydNmtzEosVmMCRtpiXW
+QFItCxC3TeQ9HXFNJGn3rHkOfHlSrQRtlZkG7XmLAoGACVKIJSoI2Kvd913TATb4
+whzNqbdiyFCadLf9cST7sGZ+ZvtHF3CT3iU/9JpBhndu4IqgFPPk694GXhoR2LK8
+SoGR4mhRvPAUgvKjRE2dypQtytgA8gm1soofSjSdoJlSBQkaxfT8N0mwQTbsoyWN
+zUDUWl3epDaqpUsV8NOuy4E=
+-----END PRIVATE KEY-----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/id_rsa.pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2d3XMIA8GTEQcUFCOm31M5jt6lGjN61ZYGnXBVBjEcyJynB7Y3C437cDpjmvbSWF1oSVVDTwMERwnXzixLG//7w8K7i6aJLKpHKtS91qnrQidmrUWDnQ4kx8AZxaN46nhSsf+cZ0nKp03ZjjR5WxeDimiDLsSUbdDmFE6ZsL2+k5OStvcqu/skUVfPe+FGTGJgIw3DyErxM7J72jUkLJXMiZkYbB1QD05k3g2LOiPqJ73QoJVGgj7YagTSA3Lgy3s/6U7IMHMV4lsaXShv1Lk/eCfIJVSaVXQRjV9KKM3wgg6PmWqwGkAO36w3eJiW1kmYKfnAM/+I5Gfo/TiNZTX uabhnil@elxadlj3q32
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/ssh_host_rsa_key
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/ssh_host_rsa_key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCuvOQFQAxi8Cyj
+iFHv2+Y1vE1sti3tChy6RuIJY6rb0dC+ptLOP85ITVCDoL9pNuCG92m/FtoW3dqp
+1augXQ+FVWgSwljvpsjtGyhsh73Mj9nd2RBGZbcjKMe+lUHDLzh8pnTkDwF3XSRx
+rUZ+tBeLsuQVnkVGPFqsXMyiMZV6xZxktqBOE8bRACNLDawb6iqXhigh5qBJ1/e3
+M44X7Ti9ymCoJG6CyxzDXuUjVHCrScze2FQGBJYNFuPOaRaj2Llalz1kpLXPgA0R
+P4jaEZbs8QDpcBckoLqchZ1UiJ7QY3TYL+aLjk3JM+fnYj9MkoUC0ihnx9uscR6l
+ftX8O3sNAgMBAAECggEADXIlja3fBiH7HV5ZB78BGPNzdcETCaF0knTv4c8Uj7O2
+f2Uw5YQNaIzifC57bCo33srdDUJB5+6Ma/MwMLfYgOcQihkAZPiNj4k+dBOB7GLJ
+XgPc973N+NujeyvbEpzomNbqOb5Z24iETGcL/KX5BdvvJya1p/Du/UJq0LRGD1tS
+0TycwcphU9rOffTCUd0+XpPJO0RehkIoyDY5PGu9rzTHkluhSotldfjVWpWaqqhq
+QQ7c04aWGsjMg4HzrqnLx35/rCKU3+tRwZ4wnAHxpOtg/EuQJiX08Z4wMwsBG+GM
+ybnd/pRAUOkvatjRemdqqtmpL0qtsmhNANaUyPc6IQKBgQDgUvd9trgVgbN/tP+b
+bFFILmqumvSA2fWZknmhRYaIcHCMAcLsRVZqlyOyRpztavOcgEmJXajPPFA6AjKa
+5g70tf3kbpveeuRaFVepSLIKSl0xIT9hV8CIxzdnRA3P4j9xsQ9Qpnuiwo9mbgFG
+lQ28nCPhW+3mNfBNmU+ZWak0dQKBgQDHaXGroc74attrQDODvChuedny5lm91n5C
+nGAaEfVHH3zrYoz65VisnvERSU1Nh8G12moldCcaWnOMY97OJmMnG/sCBZskDzRp
+e1Mf+gT0TQoyYZHMTZtA1HyRRkdTlLZ7S77HUNTK8qrIpJEHLFSnzCPlBkY84fgw
++8IdVkX5OQKBgCDnapARFi1paffoh7m3iLCqxlE4P3cLAYB2QMsMFLC8tXWD6KCZ
+hxR5eO30d55HmtYw5xh0GYfUU/w+SEf6SOVSMJyqMMjQg+BG0yXsmNjzkXncY5yW
+r5IgjpriG5iLmjzF+PYehXIZUcl3h05gHLS2vniW8G1dKhNn0oou4aflAoGAFTLh
+caR+8yuw7cLidxOunKf5gnf4fFTsETq8gKj+ETSIvCE66YUuGxO+ft7zB9XxwtpY
+RGkHqyaIeBk522J7UfIIiht8daXkJX6FxLV4h1wVRGvY6wYpBghQwcTd2kXJ7GuN
++XRfWr/XZgMQo9mTmk76VeOH3fsLvnFVHndIcwkCgYBLntA0osVpZm6egw26+80C
+PtnSrUmsW4sTB+eQbbyDn6i/fAgGKc+2WuvcdorqyfLSEcs+hE/59roFVFpCEPN5
+4oO7o+o0SQ2ehxY+Lv2XF+TnfUQlAc6BCBfK3tG6rROUFiznAaua1hcsoAa9x/LH
+0SgWzYqbWI0qq7pv91tBdw==
+-----END PRIVATE KEY-----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/ssh_host_rsa_key.pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/pkcs8/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuvOQFQAxi8CyjiFHv2+Y1vE1sti3tChy6RuIJY6rb0dC+ptLOP85ITVCDoL9pNuCG92m/FtoW3dqp1augXQ+FVWgSwljvpsjtGyhsh73Mj9nd2RBGZbcjKMe+lUHDLzh8pnTkDwF3XSRxrUZ+tBeLsuQVnkVGPFqsXMyiMZV6xZxktqBOE8bRACNLDawb6iqXhigh5qBJ1/e3M44X7Ti9ymCoJG6CyxzDXuUjVHCrScze2FQGBJYNFuPOaRaj2Llalz1kpLXPgA0RP4jaEZbs8QDpcBckoLqchZ1UiJ7QY3TYL+aLjk3JM+fnYj9MkoUC0ihnx9uscR6lftX8O3sN uabhnil@elxadlj3q32
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/auth_keys
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/auth_keys
@@ -0,0 +1,7 @@
+command="dump /home",no-pty,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
+
+command="dump /home",no-pty,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH comment with whitespaces
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH comment with whitespaces
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/known_hosts
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/known_hosts
@@ -0,0 +1,8 @@
+hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM=
+
+|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= foo@bar.com
+
+hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= Comment with whitespaces
+
+|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= foo@bar.com Comment with whitespaces
+
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_dsa_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_dsa_pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_dsa_with_comment_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_dsa_with_comment_pub
@@ -0,0 +1,3 @@
+#This should be ignored!!
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_ecdsa_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_ecdsa_pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIJrVlKYIT+MlxxRx5BFXisHHkcGMAAKv2dguUeOsutsYyzs9JAczvl6c+Sypra5+qOi2LHPXw6GGluuXcOssOM= uabhnil@elxadlj3q32
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_rsa_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/openssh_rsa_pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh1_auth_keys
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh1_auth_keys
@@ -0,0 +1,9 @@
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
+
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+
+command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
+
+command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh1_known_hosts
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh1_known_hosts
@@ -0,0 +1,3 @@
+hostname.domain.com,192.168.0.1 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+hostname2.domain.com,192.168.0.2 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
+hostname3.domain.com,192.168.0.3 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH comment with whitespaces
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_dsa_comment_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_dsa_comment_pub
@@ -0,0 +1,13 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is my public key for use on \
+servers which I don't like.
+AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET
+W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH
+YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c
+vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf
+J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA
+vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB
+AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS
+n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5
+sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_dsa_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_dsa_pub
@@ -0,0 +1,12 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: DSA Public Key for use with MyIsp
+AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET
+W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH
+YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c
+vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf
+J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA
+vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB
+AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS
+n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5
+sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_ecdsa_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_ecdsa_pub
@@ -0,0 +1,6 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "256-bit ECDSA, converted by uabhnil@elxadlj3q32 from OpenSSH"
+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIJrVlKYIT+MlxxRx5
+BFXisHHkcGMAAKv2dguUeOsutsYyzs9JAczvl6c+Sypra5+qOi2LHPXw6GGluuXcOssOM=
+
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_rsa_comment_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_rsa_comment_pub
@@ -0,0 +1,7 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "1024-bit RSA, converted from OpenSSH by me@example.com"
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_rsa_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_rsa_pub
@@ -0,0 +1,13 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o
+39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS
+7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmt
+isaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2
+sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRu
+LDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368
++dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNW
+jeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4f
+uKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV22
+5JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRM
+IB+X+OTUUI8=
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_subject_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh2_subject_pub
@@ -0,0 +1,8 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Subject: me
+Comment: 1024-bit rsa, created by me@example.com Mon Jan 15 \
+08:31:24 2001
+AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
+596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
+soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh_rsa_long_comment_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh_rsa_long_comment_pub
@@ -0,0 +1,9 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is an example of a very very very very looooooooooooo\
+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\
+comment
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh_rsa_long_header_pub
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE_data/public_key/ssh_rsa_long_header_pub
@@ -0,0 +1,9 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is an example of a very very very very looooooooooooo\
+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\
+comment
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
Index: otp-OTP-23.3.4.19/lib/ssh/test/Makefile
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/Makefile
+++ otp-OTP-23.3.4.19/lib/ssh/test/Makefile
@@ -1,7 +1,7 @@
 #
 # %CopyrightBegin%
 #
-# Copyright Ericsson AB 2004-2018. All Rights Reserved.
+# Copyright Ericsson AB 2004-2023. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -26,11 +26,12 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
 # ----------------------------------------------------
 
 MODULES= \
+	ssh_cth \
 	ssh_algorithms_SUITE \
-	ssh_options_SUITE \
 	ssh_basic_SUITE \
 	ssh_bench_SUITE \
 	ssh_chan_behaviours_SUITE \
+	ssh_collect_labmachine_info_SUITE \
 	ssh_compat_SUITE \
 	ssh_connection_SUITE \
 	ssh_agent_mock_server \
@@ -97,6 +98,7 @@ RELSYSDIR = $(RELEASE_PATH)/ssh_test
 INCLUDES = -I$(ERL_TOP)/lib/ssh/src
 
 ERL_COMPILE_FLAGS += $(INCLUDES) -pa ../ebin
+ERL_COMPILE_FLAGS := $(filter-out +deterministic,$(ERL_COMPILE_FLAGS))
 
 EBIN = .
 
@@ -104,7 +106,7 @@ EBIN = .
 # Targets
 # ----------------------------------------------------
 
-tests debug opt: emakebuild $(TARGET_FILES)
+tests $(TYPES): emakebuild $(TARGET_FILES)
 
 .PHONY: emakebuild
 
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_agent_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_agent_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_agent_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2019. All Rights Reserved.
+%% Copyright Ericsson AB 2019-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -173,6 +173,7 @@ chk_unix_domain_socket(Config0) ->
                     file:delete(SocketPath),
                     {skip, "Unix Domain Sockets are not supported"};
                 {ok, Socket} ->
+                    ct:log("Socket = ~p", [Socket]),
                     gen_tcp:close(Socket),
                     file:delete(SocketPath),
                     Config
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_agent_mock_server.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_agent_mock_server.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_agent_mock_server.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2022. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -149,7 +149,7 @@ check_mktemp(Config) ->
     end.
 
 extract_pubkey(PrivKey) ->
-    PubKey = ssh_transport:extract_public_key(PrivKey),
+    PubKey = ssh_file:extract_public_key(PrivKey),
     ssh_message:ssh2_pubkey_encode(PubKey).
 
 sig_format('ssh-rsa') -> <<"ssh-rsa">>;
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_algorithms_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -86,6 +86,24 @@ groups() ->
 	 || {Tag,Algs} <- ErlAlgos ++ DoubleAlgos,
 	    Alg <- Algs],
 
+    ct:log(
+      "ErlAlgos = ~p~n"
+      "SshcAlgos = ~p~n"
+      "SshdAlgos = ~p~n"
+      "DoubleAlgos = ~p~n"
+      "TypeSSH = ~p~n"
+      "TagGroupSet = ~p~n"
+      "AlgoTcSet = ~p~n"
+          ,[
+            ErlAlgos,
+            SshcAlgos,
+            SshdAlgos,
+            DoubleAlgos,
+            TypeSSH,
+            TagGroupSet,
+            AlgoTcSet
+           ]),
+
     TagGroupSet ++ AlgoTcSet.
 
 tags() -> [kex,cipher,mac,compression,public_key].
@@ -146,50 +164,76 @@ init_per_group(Group, Config) ->
 	    Tag = proplists:get_value(name,
 				      hd(proplists:get_value(tc_group_path, Config))),
 	    Alg = Group,
-            init_per_group(Tag, Alg, Config)
+            Algs = split(Tag, Alg),
+            SupportedAlgs = proplists:get_value(Tag, ssh_transport:supported_algorithms()),
+            PA =
+                case Algs of
+                    [_] ->
+                        [Alg];
+                    [A1,A2] when Tag == public_key ->
+                        [A1,A2];
+                    [A1,A2] ->
+                        [{client2server,[A1]},
+                         {server2client,[A2]}]
+                end,
+            case lists:foldl(fun({K,As}, Acc) ->
+                                     ct:log("~p:~p  K=~p, As=~p, SupportedAlgs=~p", [?MODULE,?LINE,K,As,SupportedAlgs]),
+                                     SAs = proplists:get_value(K,SupportedAlgs),
+                                     lists:foldl(fun(A1, Acc1) -> 
+                                                         case lists:member(A1, SAs) of
+                                                             true -> Acc1;
+                                                             false -> [A1|Acc1]
+                                                         end
+                                                 end, Acc, As);
+                                (A, Acc) when is_atom(hd(SupportedAlgs)) ->
+                                     ct:log("~p:~p  A=~p, SupportedAlgs=~p", [?MODULE,?LINE,A,SupportedAlgs]),
+                                     case lists:member(A, SupportedAlgs) of
+                                         true -> Acc;
+                                         false -> [A|Acc]
+                                     end;
+                                (A, Acc) when is_tuple(hd(SupportedAlgs)) ->
+                                     ct:log("~p:~p  A=~p, SupportedAlgs=~p", [?MODULE,?LINE,A,SupportedAlgs]),
+                                     [{_,S1},{_,S2}] = SupportedAlgs,
+
+                                     case lists:member(A, S1) andalso
+                                         lists:member(A, S2) of
+                                         true -> Acc;
+                                         false -> [A|Acc]
+                                     end
+                             end, [], PA) of
+                [] ->
+                    init_per_group(Tag, Algs, Alg, PA, Config);
+                L ->
+                    ct:log("~p:~p  Tag ~p, Alg ~p, Algs ~p, PA ~p,~nSupportedAlgs ~p", [?MODULE,?LINE, Tag, Alg, Algs, PA, SupportedAlgs]),
+                    {skip,io_lib:format("Unsupported ~p: ~p", [Tag,L])}
+            end
     end.
 
 
-init_per_group(public_key=Tag, Alg, Config) ->
-    PA =
-        case split(Tag, Alg) of
-            [_] ->
-                [Alg];
-            [A1,A2] ->
-                [A1,A2]
-        end,
-    OtherAlgs = [{T,L} || {T,L} <- ssh_transport:supported_algorithms(), T=/=Tag],
-    ct:log("Init tests for public_key ~p~nOtherAlgs=~p",[PA,OtherAlgs]),
-    PrefAlgs = {preferred_algorithms,[{Tag,PA}|OtherAlgs]},
-    %% Daemon started later in init_per_testcase
-    try
-        setup_pubkey(PA,
-                 [{pref_algs,PrefAlgs},
-                  {tag_alg,{Tag,PA}}
-                  | Config])
-    catch
-        _C:_E:_S ->
-            ct:log("Exception ~p:~p~n~p",[_C,_E,_S]),
-            {skip, io_lib:format("Unsupported: ~p",[Alg])}
-    end;
-
-init_per_group(Tag, Alg, Config) ->
-    PA =
-        case split(Tag, Alg) of
-            [_] ->
-                [Alg];
-            [A1,A2] ->
-                [{client2server,[A1]},
-                 {server2client,[A2]}]
-        end,
+init_per_group(Tag, Algs, Alg, PA, Config) ->
     OtherAlgs = [{T,L} || {T,L} <- ssh_transport:supported_algorithms(), T=/=Tag],
-    ct:log("Init tests for tag=~p alg=~p~nOtherAlgs=~p",[Tag,PA,OtherAlgs]),
+    ct:log("init_per_group Tag ~p, Alg ~p, Algs ~p ,PA ~p,~nOtherAlgs ~p", [Tag, Alg, Algs, PA, OtherAlgs]),
     PrefAlgs = {preferred_algorithms,[{Tag,PA}|OtherAlgs]},
-    start_std_daemon([PrefAlgs],
-                     [{pref_algs,PrefAlgs},
-                      {tag_alg,{Tag,[Alg]}}
-                      | Config]).
+    case Tag of
+        public_key ->
+            %% Daemon started later in init_per_testcase
+            try
+                setup_pubkey(PA,
+                             [{pref_algs,PrefAlgs},
+                              {tag_alg,{Tag,PA}}
+                              | Config])
+            catch
+                _C:_E:_S ->
+                    ct:log("Exception ~p:~p~n~p",[_C,_E,_S]),
+                    {skip, io_lib:format("Unsupported: ~p",[Alg])}
+            end;
 
+        _ ->
+            start_std_daemon([PrefAlgs],
+                             [{pref_algs,PrefAlgs},
+                              {tag_alg,{Tag,[Alg]}}
+                              | Config])
+    end.
 
 end_per_group(_Alg, Config) ->
     case proplists:get_value(srvr_pid,Config) of
@@ -364,6 +408,7 @@ sshc_simple_exec_os_cmd(Config) ->
                                                         " -o UserKnownHostsFile=",KnownHosts,
                                                         " -o CheckHostIP=no"
                                                         " -o StrictHostKeyChecking=no"
+                                                        " -o UpdateHostKeys=no"
                                                         " -q"
                                                         " -x"
                                                         ],
@@ -393,10 +438,9 @@ sshd_simple_exec(Config) ->
             {public_key,Alg} -> [{pref_public_key_algs,Alg}];
             _ -> []
         end,
-    ConnectionRef = ssh_test_lib:connect(22, [{silently_accept_hosts, true},
-                                              proplists:get_value(pref_algs,Config),
-					      {user_interaction, false}
-                                              | ClientPubKeyOpts]),
+    ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT,
+                                         [proplists:get_value(pref_algs,Config)
+                                          | ClientPubKeyOpts]),
     {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
     success = ssh_connection:exec(ConnectionRef, ChannelId0,
 				  "echo testing", infinity),
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_basic_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_basic_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_basic_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 
 -module(ssh_basic_SUITE).
 
+-include_lib("public_key/include/public_key.hrl").
 -include_lib("common_test/include/ct.hrl").
 -include_lib("kernel/include/inet.hrl").
 -include_lib("kernel/include/file.hrl").
@@ -73,6 +74,7 @@
          login_bad_pwd_no_retry3/1,
          login_bad_pwd_no_retry4/1,
          login_bad_pwd_no_retry5/1,
+         max_initial_idle_time/1,
          misc_ssh_options/1,
          multi_daemon_opt_fd/1,
          openssh_zlib_basic_test/1,
@@ -154,7 +156,9 @@ groups() ->
                              exec, exec_compressed, 
                              exec_with_io_out, exec_with_io_in,
                              cli, cli_exit_normal, cli_exit_status,
-                             idle_time_client, idle_time_server, openssh_zlib_basic_test, 
+                             idle_time_client, idle_time_server,
+                             max_initial_idle_time,
+                             openssh_zlib_basic_test,
                              misc_ssh_options, inet_option, inet6_option,
                              shell, shell_socket, shell_ssh_conn, shell_no_unicode, shell_unicode_string,
                              close
@@ -242,16 +246,16 @@ end_per_testcase(_Config) ->
 %%--------------------------------------------------------------------
 %%% Application consistency test.
 app_test(Config) when is_list(Config) ->
-    ?t:app_test(ssh),
+    test_server:app_test(ssh),
     ok.
 %%--------------------------------------------------------------------
 %%% Appup file consistency test.
 appup_test(Config) when is_list(Config) ->
-    ok = ?t:appup_test(ssh).
+    ok = test_server:appup_test(ssh).
 %%--------------------------------------------------------------------
 %%% Test that we can set some misc options not tested elsewhere
 %%% some options not yet present are not decided if we should support or
-%%% if they need thier own test case.
+%%% if they need their own test case.
 misc_ssh_options(Config) when is_list(Config) ->  
     SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
     UserDir = proplists:get_value(priv_dir, Config),
@@ -471,6 +475,25 @@ idle_time_common(DaemonExtraOpts, Client
     ssh:stop_daemon(Pid).
 
 %%--------------------------------------------------------------------
+max_initial_idle_time(Config) ->
+    SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
+    UserDir = proplists:get_value(priv_dir, Config),
+
+    {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+					     {user_dir, UserDir},
+					     {failfun, fun ssh_test_lib:failfun/2},
+                                             {max_initial_idle_time, 2000}
+                                            ]),
+    ConnectionRef =
+	ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+					  {user_dir, UserDir},
+					  {user_interaction, false}
+                                         ]),
+    timer:sleep(8000),
+    {error, closed} = ssh_connection:session_channel(ConnectionRef, 1000),
+    ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
 %%% Test that ssh:shell/2 works
 shell(Config) when is_list(Config) ->
     process_flag(trap_exit, true),
@@ -520,7 +543,7 @@ shell_socket(Config) when is_list(Config
     ct:log("~p:~p udp socket failed ok", [?MODULE,?LINE]),
     gen_udp:close(BadSock),
 
-    %% And finaly test with passive mode (which should work):
+    %% And finally test with passive mode (which should work):
     IO = ssh_test_lib:start_io_server(),
     {ok,Sock} = gen_tcp:connect(Host, Port, [{active,false}]),
     Shell = ssh_test_lib:start_shell(Sock, IO, [{user_dir,UserDir}]),
@@ -550,7 +573,7 @@ shell_ssh_conn(Config) when is_list(Conf
     ct:sleep(500),
 
     IO = ssh_test_lib:start_io_server(),
-    {ok,C} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+    C = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
                                       {user_dir, UserDir},
                                       {user_interaction, false}]),
     Shell = ssh_test_lib:start_shell(C, IO, undefined),
@@ -659,7 +682,7 @@ cli_exit_status(Config) when is_list(Con
 
 %%--------------------------------------------------------------------
 %%% Test that get correct error message if you try to start a daemon
-%%% on an adress that already runs a daemon see also seq10667
+%%% on an address that already runs a daemon see also seq10667
 daemon_already_started(Config) when is_list(Config) ->
     SystemDir = proplists:get_value(data_dir, Config),
     UserDir = proplists:get_value(priv_dir, Config),
@@ -716,7 +739,9 @@ known_hosts(Config) when is_list(Config)
     ConnectionRef =
 	ssh_test_lib:connect(Host, Port, [{user_dir, PrivDir},
 					  {user_interaction, false},
-					  silently_accept_hosts]),
+					  {silently_accept_hosts, true},
+                                          {save_accepted_host, true}
+                                         ]),
     {ok, _Channel} = ssh_connection:session_channel(ConnectionRef, infinity),
     ok = ssh:close(ConnectionRef),
     {ok, Binary} = file:read_file(KnownHosts),
@@ -745,7 +770,9 @@ known_hosts(Config) when is_list(Config)
     _ConnectionRef2 =
 	ssh_test_lib:connect(Host, Port, [{user_dir, PrivDir},
 					  {user_interaction, false},
-					  silently_accept_hosts]),
+					  {silently_accept_hosts, true},
+                                          {save_accepted_host, true}
+                                         ]),
     {ok, Binary2} = file:read_file(KnownHosts),
     case Binary of
         Binary2 -> ok;
@@ -758,7 +785,9 @@ known_hosts(Config) when is_list(Config)
      _ConnectionRef3 =
 	ssh_test_lib:connect(Host, Port, [{user_dir, PrivDir},
 					  {user_interaction, false},
-					  silently_accept_hosts]),
+					  {silently_accept_hosts, true},
+                                          {save_accepted_host, true}
+                                         ]),
     ct:log("New known_hosts:~n~p",[Binary3]),
     {ok, Binary4} = file:read_file(KnownHosts),
     case Binary3 of
@@ -777,12 +806,14 @@ ssh_file_is_host_key(Config) ->
     ct:log("Dir = ~p", [Dir]),
     KnownHosts = filename:join(Dir, "known_hosts"),
 
-    Key1 = {ed_pub,ed25519,<<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,
-                             214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
-    Key2 = {ed_pub,ed448,<<95,215,68,155,89,180,97,253,44,231,135,236,97,106,212,106,29,
-                           161,52,36,133,167,14,31,138,14,167,93,128,233,103,120,237,241,
-                           36,118,155,70,199,6,27,214,120,61,241,229,15,108,209,250,26,
-                           190,175,232,37,97,128>>},
+    Key1 = {#'ECPoint'{point = <<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,
+                                 214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
+            {namedCurve,?'id-Ed25519'}},
+    Key2 = {#'ECPoint'{point = <<95,215,68,155,89,180,97,253,44,231,135,236,97,106,212,106,29,
+                                 161,52,36,133,167,14,31,138,14,167,93,128,233,103,120,237,241,
+                                 36,118,155,70,199,6,27,214,120,61,241,229,15,108,209,250,26,
+                                 190,175,232,37,97,128>>},
+            {namedCurve,?'id-Ed448'}},
     Key3 = {'RSAPublicKey',26565213557098441060571713941539431805641814292761836797158846333985276408616038302348064841541244792430014595960643885863857366044141899534486816837416587694213836843799730043696945690516841209754307951050689906601353687467659852190777927968674989320642319504162787468947018505175948989102544757855693228490011564030927714896252701919941617689227585365348356580525802093985552564228730275431222515673065363441446158870936027338182083252824862151536327733046243804704721201548991176621134884093279416695997338124856506800535228380202243308550318880784741179703553922258881924287662178348044420509921666661119986374777,
             65537},
 
@@ -825,13 +856,14 @@ ssh_file_is_host_key_misc(Config) ->
     ct:log("Dir = ~p", [Dir]),
     KnownHosts = filename:join(Dir, "known_hosts"),
 
-    Key1 = {ed_pub,ed25519,<<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,
-                             214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
-    Key2 = {ed_pub,ed448,<<95,215,68,155,89,180,97,253,44,231,135,236,97,106,212,106,29,
-                           161,52,36,133,167,14,31,138,14,167,93,128,233,103,120,237,241,
-                           36,118,155,70,199,6,27,214,120,61,241,229,15,108,209,250,26,
-                           190,175,232,37,97,128>>},
-
+    Key1 = {#'ECPoint'{point = <<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,
+                                 214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
+            {namedCurve,?'id-Ed25519'}},
+    Key2 = {#'ECPoint'{point = <<95,215,68,155,89,180,97,253,44,231,135,236,97,106,212,106,29,
+                                 161,52,36,133,167,14,31,138,14,167,93,128,233,103,120,237,241,
+                                 36,118,155,70,199,6,27,214,120,61,241,229,15,108,209,250,26,
+                                 190,175,232,37,97,128>>},
+            {namedCurve,?'id-Ed448'}},
     FileContents = <<"h11,h12,!h12 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIElI66JgZZo72XJ7wGBp+h3WTDw/pxXddvaomAIHrIl9\n",
                      %% Key revoked later in file:
                      "h22 ssh-ed448 AAAACXNzaC1lZDQ0OAAAADlf10SbWbRh/Sznh+xhatRqHaE0JIWnDh"
@@ -865,13 +897,14 @@ ssh_file_is_auth_key(Config) ->
     ct:log("Dir = ~p", [Dir]),
     AuthKeys = filename:join(Dir, "authorized_keys"),
 
-    Key1 = {ed_pub,ed25519,<<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,
-                             214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
-    Key2 = {ed_pub,ed448,<<95,215,68,155,89,180,97,253,44,231,135,236,97,106,212,106,29,
-                           161,52,36,133,167,14,31,138,14,167,93,128,233,103,120,237,241,
-                           36,118,155,70,199,6,27,214,120,61,241,229,15,108,209,250,26,
-                           190,175,232,37,97,128>>},
-
+    Key1 = {#'ECPoint'{point = <<73,72,235,162,96,101,154,59,217,114,123,192,96,105,250,29,
+                                 214,76,60,63,167,21,221,118,246,168,152,2,7,172,137,125>>},
+            {namedCurve,?'id-Ed25519'}},
+    Key2 = {#'ECPoint'{point = <<95,215,68,155,89,180,97,253,44,231,135,236,97,106,212,106,29,
+                                 161,52,36,133,167,14,31,138,14,167,93,128,233,103,120,237,241,
+                                 36,118,155,70,199,6,27,214,120,61,241,229,15,108,209,250,26,
+                                 190,175,232,37,97,128>>},
+            {namedCurve,?'id-Ed448'}},
     FileContents = <<" \n",
                      "# A test file\n",
                      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIElI66JgZZo72XJ7wGBp+h3WTDw/pxXddvaomAIHrIl9 foo@example.com\n",
@@ -979,6 +1012,7 @@ internal_error(Config) when is_list(Conf
 
     {error, Error} =
         ssh:connect(Host, Port, [{silently_accept_hosts, true},
+                                 {save_accepted_host, false},
                                  {user_dir, UserDir},
                                  {user_interaction, false}]),
     check_error(Error),
@@ -1091,7 +1125,7 @@ double_close(Config) when is_list(Config
 					     {user_dir, UserDir},
 					     {user_passwords, [{"vego", "morot"}]},
 					     {failfun, fun ssh_test_lib:failfun/2}]),
-    {ok, CM} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+    CM = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
 					   {user_dir, UserDir},
 					    {user, "vego"},
 					    {password, "morot"},
@@ -1108,6 +1142,8 @@ daemon_opt_fd(Config) ->
     file:make_dir(UserDir),
 
     {ok,S1} = gen_tcp:listen(0,[]),
+    ct:log("Socket S1 = ~p", [S1]),
+    
     {ok,Fd1} = prim_inet:getfd(S1),
     
     {ok,Pid1} = ssh:daemon(0, [{system_dir, SystemDir},
@@ -1117,7 +1153,7 @@ daemon_opt_fd(Config) ->
 			       {failfun, fun ssh_test_lib:failfun/2}]),
     
     {ok,{_Host1,Port1}} = inet:sockname(S1),
-    {ok, C1} = ssh:connect("localhost", Port1, [{silently_accept_hosts, true},
+    C1 = ssh_test_lib:connect(Port1, [{silently_accept_hosts, true},
 					  {user_dir, UserDir},
 					  {user, "vego"},
 					  {password, "morot"},
@@ -1137,6 +1173,7 @@ multi_daemon_opt_fd(Config) ->
     Test = 
 	fun() ->
 		{ok,S} = gen_tcp:listen(0,[]),
+                ct:log("Socket S = ~p", [S]),
 		{ok,Fd} = prim_inet:getfd(S),
 
 		{ok,Pid} = ssh:daemon(0, [{system_dir, SystemDir},
@@ -1146,7 +1183,7 @@ multi_daemon_opt_fd(Config) ->
 					  {failfun, fun ssh_test_lib:failfun/2}]),
 
 		{ok,{_Host,Port}} = inet:sockname(S),
-		{ok, C} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
+		C = ssh_test_lib:connect(Port, [{silently_accept_hosts, true},
 							  {user_dir, UserDir},
 							  {user, "vego"},
 							  {password, "morot"},
@@ -1194,7 +1231,7 @@ packet_size(Config) ->
 
 rec(Server, Conn, Ch, MaxSz) ->
     receive
-        {ssh_cm,Conn,{data,Ch,_,M}} when size(M) =< MaxSz ->
+        {ssh_cm,Conn,{data,Ch,_,M}} when byte_size(M) =< MaxSz ->
             ct:log("~p: ~p",[MaxSz,M]),
             rec(Server, Conn, Ch, MaxSz);
         {ssh_cm,Conn,{data,Ch,_,_}} = M ->
@@ -1383,7 +1420,7 @@ login_bad_pwd_no_retry(Config, AuthMetho
 		{ok,Conn} ->
 		    ssh:close(Conn),
 		    ssh:stop_daemon(DaemonRef),
-		    {fail, "Connect erroneosly succeded"}
+		    {fail, "Connect erroneosly succeeded"}
 	    end
     end.
 
@@ -1455,7 +1492,7 @@ setopts_getopts(Config) ->
 %% Internal functions ------------------------------------------------
 %%--------------------------------------------------------------------
 %% Due to timing the error message may or may not be delivered to
-%% the "tcp-application" before the socket closed message is recived
+%% the "tcp-application" before the socket closed message is received
 check_error("Invalid state") -> ok;
 check_error("Connection closed") -> ok;
 check_error("Selection of key exchange algorithm failed"++_) -> ok;
@@ -1467,7 +1504,7 @@ basic_test(Config) ->
     ServerOpts = proplists:get_value(server_opts, Config),
     
     {Pid, Host, Port} = ssh_test_lib:daemon(ServerOpts),
-    {ok, CM} = ssh:connect(Host, Port, ClientOpts),
+    CM = ssh_test_lib:connect(Host, Port, ClientOpts),
     ok = ssh:close(CM),
     ssh:stop_daemon(Pid).
 
@@ -1505,7 +1542,7 @@ new_do_shell(IO, N, [new_prompt|More]) -
 
 new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
     Pfx = prompt_prefix(),
-    PfxSize = size(Pfx),
+    PfxSize = byte_size(Pfx),
     receive
 	_X = <<"\r\n">> ->
 	    ct:log("Skip newline ~p",[_X]),
@@ -1547,7 +1584,8 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|Mor
 		    ct:log("Matched echo ~ts",[RecStr]),
 		    new_do_shell(IO, N, More);
 		false ->
-		    ct:fail("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr])
+		    ct:log("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr]),
+            new_do_shell(IO, N, Ops)
 	    end
     after 30000 ->
 	    ct:log("Message queue of ~p:~n~p",
@@ -1572,7 +1610,7 @@ prompt_prefix() ->
 new_do_shell_prompt(IO, N, type, Str, More) ->
     ct:log("Matched prompt ~p to trigger sending of next line to server",[N]),
     IO ! {input, self(), Str++"\r\n"},
-    ct:log("Promt '~p> ', Sent ~ts",[N,Str++"\r\n"]),
+    ct:log("Prompt '~p> ', Sent ~ts",[N,Str++"\r\n"]),
     new_do_shell(IO, N, More);
 new_do_shell_prompt(IO, N, Op, Str, More) ->
     ct:log("Matched prompt ~p",[N]),
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_bench_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_bench_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_bench_SUITE.erl
@@ -1,7 +1,7 @@
 %%%-------------------------------------------------------------------
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2015-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -156,6 +156,7 @@ transfer_text(Config) ->
                {password, proplists:get_value(pwd,      Config)},
                {user_dir, proplists:get_value(priv_dir, Config)},
                {silently_accept_hosts, true},
+               {save_accepted_host, false},
                {user_interaction, false},
                {max_random_length_padding, 0}
               ],
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2018-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2018-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -90,7 +90,6 @@ end_per_testcase(_TC, Config) ->
     
 
 -define(EXPECT(What, Bind),
-        Bind =
             (fun() ->
                      receive What ->
                              ct:log("~p:~p ~p got ~p",[?MODULE,?LINE,self(),What]),
@@ -105,7 +104,7 @@ end_per_testcase(_TC, Config) ->
 %%--------------------------------------------------------------------
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
-%% Try start a subsystem whos name is not known by the server
+%% Try start a subsystem whose name is not known by the server
 noexist_subsystem(Config) ->
     C = proplists:get_value(connref, Config),
     {ok, Ch} = ssh_connection:session_channel(C, infinity),
@@ -129,20 +128,20 @@ defined_subsystem(Config) ->
     {ok, Ch1} = ssh_connection:session_channel(C, infinity),
 
     success = ssh_connection:subsystem(C, Ch1, "ch1", infinity),
-    IDsrv = ?EXPECT({{_Csrv,_Ch1srv}, {ssh_channel_up,_Ch1srv,_Csrv}}, {_Csrv,_Ch1srv}),
+    IDsrv = ?EXPECT({{Csrv,Ch1srv}, {ssh_channel_up,Ch1srv,Csrv}}, {Csrv,Ch1srv}),
 
     ok = ssh_connection:close(C, Ch1),
     ?EXPECT({IDsrv, {terminate,normal}}, []),
     ?EXPECT({ssh_cm, C, {closed,Ch1}}, []), % self() is instead of a proper channel handler
     ok.
 
-%% Try to start and stop a subsystem from a ssh_client_channel behviour
+%% Try to start and stop a subsystem from a ssh_client_channel behaviour
 subsystem_client(Config) ->
     C = proplists:get_value(connref, Config),
 
     {ok,ChRef} = ssh_chan_behaviours_client:start_link(C),
-    IDclt = ?EXPECT({{C,_Ch1clt},     {ssh_channel_up,_Ch1clt,C}},     {C,_Ch1clt}),
-    IDsrv = ?EXPECT({{_Csrv,_Ch1srv}, {ssh_channel_up,_Ch1srv,_Csrv}}, {_Csrv,_Ch1srv}),
+    IDclt = ?EXPECT({{C,Ch1clt},     {ssh_channel_up,_C1clt,C}},     {C,Ch1clt}),
+    IDsrv = ?EXPECT({{Csrv,_h1srv}, {ssh_channel_up,Ch1srv,Csrv}}, {Csrv,Ch1srv}),
 
     ok = ssh_chan_behaviours_client:stop(ChRef),
     ?EXPECT({IDclt, {terminate,normal}}, []), % From the proper channel handler
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_collect_labmachine_info_SUITE.erl
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_collect_labmachine_info_SUITE.erl
@@ -0,0 +1,250 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssh_collect_labmachine_info_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+-export([save_ssh_data/3]).
+
+-export([
+         suite/0,
+         all/0,
+         init_per_suite/1,
+         end_per_suite/1,
+         ssh_info_lib/1
+        ]).
+
+
+-define(DAYS_TO_KEEP, 5).
+
+save_ssh_data(Host, Data, Config0) ->
+    case init_data_transfer(Host, Config0) of
+        Config1 when is_list(Config1) ->
+            Config =
+                case Data of
+                    [[_|_]|_] ->
+                        lists:foldl(fun save_data/2, Config1, Data);
+                    _ ->
+                        save_data(Data, Config1)
+                end,
+            end_data_transfer(Config);
+
+        Skip ->
+            Skip
+    end.
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{timetrap,{seconds,40}}].
+
+all() -> [ssh_info_lib].
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+    case os:find_executable("ssh") of
+        false ->
+            {skip, "No ssh found"};
+
+        _Path ->
+            init_data_transfer(hostname(), Config)
+    end.
+
+end_per_suite(Config) ->
+    end_data_transfer(Config).
+
+%%--------------------------------------------------------------------
+ssh_info_lib(Config) ->
+    NewEntry = new_entry(),
+    ct:pal("New entry:~n~p",[NewEntry]),
+    save_data(NewEntry, Config).
+
+new_entry() ->
+    [{hostname,           hostname()},
+     {type,               host},
+     {date,               date()},
+     {time,               time()},
+     {os_type,            os:type()},
+     {os_version,         os:version()},
+     {full_ssh_version,   ssh_info()}
+    ].
+
+ssh_info() ->
+    try os:cmd("ssh -V") of
+        Version when is_list(Version) -> Version--"\n";
+        _ -> "?"
+    catch
+        _:_ -> "??"
+    end.
+
+%%--------------------------------------------------------------------
+hostname() ->
+    case inet:gethostname() of
+	{ok,Name} -> string:to_lower(Name);
+	_ -> "undefined"
+    end.
+	    
+priv_dir(Config) -> proplists:get_value(priv_dir, Config).
+
+priv_file(Config, Name) -> filename:join(priv_dir(Config), Name).
+
+remove_drive_letter(FileName) ->
+    ssh_test_lib:winpath_to_linuxpath(FileName).
+
+usable_file(FileName) ->
+    case file:open(FileName, [append]) of
+        {ok,D} ->
+            ok == file:close(D);
+        _ ->
+            false
+    end.
+
+%%%----------------------------------------------------------------
+wsl_ify(Cmnd) ->
+    case os:getenv("WSLENV") of
+        false -> Cmnd;
+        _ -> "wsl " ++ Cmnd
+    end.
+
+%%%================================================================
+save_data(NewEntry, Config) ->
+    LocalFile = proplists:get_value(local_file, Config),
+
+    YoungEntries =
+        case file:consult(LocalFile) of
+            {ok, Consulted} when is_list(Consulted) ->
+                lists:filter(fun(E) -> is_young(E) end,
+                             Consulted);
+            Other ->
+                ct:log("Strange result of consult:~n~p", [Other]),
+                ct:fail("Consult failed")
+        end,
+
+    {ok,D} = file:open(LocalFile, [write]),
+    lists:foreach(fun(E) ->
+                          io:format(D, '~p.~n', [E])
+                  end, lists:usort([NewEntry|YoungEntries])),
+    file:close(D),
+    Config.
+
+
+is_young(E) ->
+    try
+        Days = days_ago(proplists:get_value(date, E)),
+        Days >= 0 andalso Days =< ?DAYS_TO_KEEP
+    catch
+        _:_ -> false                     % No or illegal date property
+    end.
+
+
+days_ago(D={_,_,_})->
+     calendar:date_to_gregorian_days(date()) - calendar:date_to_gregorian_days(D).
+
+%%%----------------------------------------------------------------
+init_data_transfer(Host, Config) ->
+    case ct:get_config(collect_host_info) of
+        undefined ->
+            {skip, "No 'collect_host_info' path configured"};
+
+        Root when is_list(Root) ->
+            RemoteFile = filename:join([Root, "ssh_info", Host++".data"]),
+            init_data_transfer_cont(Host, Config, RemoteFile)
+    end.
+
+init_data_transfer_cont(Host, Config, RemoteFile) ->
+    LocalFile = priv_file(Config, Host++".sshdata"),
+
+    case usable_file(LocalFile) of
+        false -> ct:fail(no_local_file);
+        true -> ok
+    end,
+
+    TransferType =
+        case {path_type(RemoteFile), os:type()} of
+            {local, {unix,_}} ->
+                case usable_file(RemoteFile) of
+                    true -> filesystem;
+                    false -> ssh
+                end;
+            _ ->
+                ssh
+        end,
+
+    case TransferType of
+        filesystem ->
+            %% 'filesystem' was concluded since it was possible
+            %% to open the file in append mode
+            {ok,B} = file:read_file(RemoteFile),
+            ok = file:write_file(LocalFile, B);
+        ssh ->
+            SCP = wsl_ify("scp "++RemoteFile++" "++remove_drive_letter(LocalFile)),
+            ct:pal("Run command: \"~s\"", [SCP]),
+            Result = os:cmd(SCP),
+            ct:pal("Command result: \"~s\"",[Result])
+    end,
+
+    [{transfer_type, TransferType},
+     {local_file,LocalFile},
+     {remote_file,RemoteFile} | Config].
+
+%%%----------------------------------------------------------------
+end_data_transfer(Config) ->
+    LocalFile = proplists:get_value(local_file,Config),
+    RemoteFile = proplists:get_value(remote_file,Config),
+    case proplists:get_value(transfer_type,Config) of
+        filesystem ->
+            {ok,B} = file:read_file(LocalFile),
+            ok = file:write_file(RemoteFile, B);
+        ssh ->
+            SCP = wsl_ify("scp "++remove_drive_letter(LocalFile)++" "++RemoteFile),
+            ct:pal("Run command: \"~s\"", [SCP]),
+            Result = os:cmd(SCP),
+            ct:pal("Command result: \"~s\"",[Result])
+    end,
+    file:delete(LocalFile).
+
+path_type(Path) ->
+    case string:lexemes(Path, ":") of
+        [_] ->
+            local;
+        [Host | _] ->
+            case string:find(Host, "/") of
+                nomatch -> remote;
+                _ -> local
+            end
+    end.
+
+
+
+
+
+
+
+
+
+
+
+
+
+    
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_compat_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_compat_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_compat_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2023. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -35,7 +35,9 @@
          init_per_suite/1,
          end_per_suite/1,
          init_per_group/2,
-         end_per_group/2
+         end_per_group/2,
+         init_per_testcase/2,
+         end_per_testcase/2
         ]).
 
 -export([
@@ -78,7 +80,7 @@ groups() ->
 ssh_image_versions() ->
     try
         %% Find all useful containers in such a way that undefined command, too low
-        %% priviliges, no containers and containers found give meaningful result:
+        %% privileges, no containers and containers found give meaningful result:
         L0 = ["REPOSITORY"++_|_] = string:tokens(os:cmd("docker images"), "\r\n"),
         [["REPOSITORY","TAG"|_]|L1] = [string:tokens(E, " ") || E<-L0],
         [list_to_atom(V) || [?DOCKER_PFX,V|_] <- L1]
@@ -98,7 +100,9 @@ init_per_suite(Config) ->
                {skip, "No docker"};
            _ ->
                ssh:start(),
+               log_image_versions(ssh_image_versions(), Config),
                ct:log("Crypto info: ~p",[crypto:info_lib()]),
+               ct:log("ssh image versions: ~p",[ssh_image_versions()]),
                Config
        end).
 
@@ -148,7 +152,7 @@ init_per_group(G, Config0) ->
                                     ct:comment("~s",[NewCmnt])
                             end,
                             AuthMethods =
-                                %% This should be obtained by quering the peer, but that
+                                %% This should be obtained by querying the peer, but that
                                 %% is a bit hard. It is possible with ssh_protocol_SUITE
                                 %% techniques, but it can wait.
                                 case Vssh of
@@ -192,6 +196,26 @@ end_per_group(G, Config) ->
             ok
     end.
 
+
+init_per_testcase(TC, Config) when TC==login_otp_is_client ; 
+				   TC==all_algorithms_sftp_exec_reneg_otp_is_client ->
+    case proplists:get_value(ssh_version, Config) of
+        "openssh4.4p1-openssl0.9.8c"  -> {skip, "Not tested"};
+        "openssh4.5p1-openssl0.9.8m"  -> {skip, "Not tested"};
+        "openssh5.0p1-openssl0.9.8za" -> {skip, "Not tested"};
+        "openssh6.2p2-openssl0.9.8c"  -> {skip, "Not tested"};
+        "openssh6.3p1-openssl0.9.8zh" -> {skip, "Not tested"};
+        "openssh6.6p1-openssl1.0.2n"  -> {skip, "Not tested"};
+        _ ->
+            Config
+    end;
+init_per_testcase(_, Config) ->
+    Config.
+        
+
+end_per_testcase(_TC, _Config) ->
+    ok.
+
 %%--------------------------------------------------------------------
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
@@ -228,6 +252,7 @@ login_otp_is_client(Config) ->
                                                  {user,?USER},
                                                  {user_dir, Dir},
                                                  {silently_accept_hosts,true},
+                                                 {save_accepted_host, false},
                                                  {user_interaction,false}
                                                  | Opts
                                                 ])
@@ -296,6 +321,7 @@ all_algorithms_sftp_exec_reneg_otp_is_cl
                                            {user_dir, new_dir(Config)},
                                            {preferred_algorithms, [{Tag,[Alg]} | PrefAlgs]},
                                            {silently_accept_hosts,true},
+                                           {save_accepted_host, false},
                                            {user_interaction,false}
                                           ])  ,
                           test_erl_client_reneg(ConnRes, % Seems that max 10 channels may be open in sshd
@@ -310,6 +336,7 @@ all_algorithms_sftp_exec_reneg_otp_is_cl
 %%--------------------------------------------------------------------
 renegotiation_otp_is_server(Config) ->
     PublicKeyAlgs = [A || {public_key,A} <- proplists:get_value(common_remote_client_algs, Config, [])],
+    ct:log("PublicKeyAlgs = ~p", [PublicKeyAlgs]),
     UserDir = setup_remote_priv_and_local_auth_keys(hd(PublicKeyAlgs), Config),
     SftpRootDir = new_dir(Config),
     ct:log("Rootdir = ~p",[SftpRootDir]),
@@ -321,6 +348,7 @@ renegotiation_otp_is_server(Config) ->
                              {user_dir, UserDir},
                              {user_passwords, [{?USER,?PASSWD}]},
                              {failfun, fun ssh_test_lib:failfun/2},
+                             {modify_algorithms, [{append, [{public_key,PublicKeyAlgs}]}]},
                              {connectfun,
                               fun(_,_,_) ->
                                       HostConnRef = self(),
@@ -364,7 +392,7 @@ reneg_tester_loop(Parent, Ref, HostConnR
 send_recv_big_with_renegotiate_otp_is_client(Config) ->
     %% Connect to the remote openssh server:
     {IP,Port} = ip_port(Config),
-    {ok,C} = ssh:connect(IP, Port, [{user,?USER},
+    C = ssh_test_lib:connect(IP, Port, [{user,?USER},
                                     {password,?PASSWD},
                                     {user_dir, setup_remote_auth_keys_and_local_priv('ssh-rsa', Config)},
                                     {silently_accept_hosts,true},
@@ -382,7 +410,7 @@ send_recv_big_with_renegotiate_otp_is_cl
     Data = << <<X:32>> || X <- lists:seq(1, HalfSizeBytes div 4)>>,
 
     %% Send the data. Must spawn a process to avoid deadlock. The client will block
-    %% until all is sent through the send window. But the server will stop receiveing
+    %% until all is sent through the send window. But the server will stop receiving
     %% when the servers send-window towards the client is full.
     %% Since the client can't receive before the server has received all but 655k from the client
     %% ssh_connection:send/4 is blocking...
@@ -473,7 +501,7 @@ loop_until(CondFun, DoFun, Acc) ->
 exec_from_docker(Config, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)),
                                                                                is_list(Config) ->
     {DockerIP,DockerPort} = ip_port(Config),
-    {ok,C} = ssh:connect(DockerIP, DockerPort,
+    C = ssh_test_lib:connect(DockerIP, DockerPort,
                          [{user,?USER},
                           {password,?PASSWD},
                           {user_dir, new_dir(Config)},
@@ -634,6 +662,7 @@ setup_remote_auth_keys_and_local_priv(Ke
                                                    {password, ?PASSWD   },
                                                    {auth_methods, "password"},
                                                    {silently_accept_hosts,true},
+                                                   {save_accepted_host, false},
                                                    {preferred_algorithms, ssh_transport:supported_algorithms()},
                                                    {user_interaction,false}
                                                   ]),
@@ -847,7 +876,7 @@ new_dir(Config) ->
 
 %%--------------------------------------------------------------------
 %%
-%% Find the intersection of algoritms for otp ssh and the docker ssh.
+%% Find the intersection of algorithms for otp ssh and the docker ssh.
 %% Returns {ok, ServerHello, Server, ClientHello, Client} where Server are the algorithms common
 %% with the docker server and analogous for Client.
 %%
@@ -1069,7 +1098,7 @@ receive_hello(S, Ack) ->
 
 
 receive_kexinit(_S, <<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>)
-  when PacketLen < 5000, % heuristic max len to stop huge attempts if packet decodeing get out of sync
+  when PacketLen < 5000, % heuristic max len to stop huge attempts if packet decoding get out of sync
        size(PayloadAndPadding) >= (PacketLen-1) % Need more bytes?
        ->
     ct:log("Has all ~p packet bytes",[PacketLen]),
@@ -1182,7 +1211,7 @@ do_check_local_directory(ServerRootDir)
 call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir, Ref) ->
     {DockerIP,DockerPort} = ip_port(Config),
     ct:log("Going to connect ~p:~p", [DockerIP, DockerPort]),
-    {ok,C} = ssh:connect(DockerIP, DockerPort,
+    C = ssh_test_lib:connect(DockerIP, DockerPort,
                          [{user,?USER},
                           {password,?PASSWD},
                           {user_dir, UserDir},
@@ -1237,13 +1266,13 @@ call_sftp_in_docker(Config, ServerIP, Se
 recv_log_msgs(C, Ch) ->
     receive
         {ssh_cm,C,{closed,Ch}} ->
-            %% ct:log("Channel closed ~p",[{closed,1}]),
+            ct:log("Channel closed ~p",[{closed,1}]),
             ok;
         {ssh_cm,C,{data,Ch,1,Msg}} ->
             ct:log("*** ERROR from docker:~n~s",[Msg]),
             recv_log_msgs(C, Ch);
         {ssh_cm,C,_Msg} ->
-            %% ct:log("Got ~p",[_Msg]),
+            ct:log("Got ~p",[_Msg]),
             recv_log_msgs(C, Ch)
     after
         30000 ->
@@ -1458,3 +1487,50 @@ renegotiate_test(Kex1, ConnectionRef) ->
             %% ct:log("Renegotiate test passed!",[]),
             ok
     end.
+
+%%%----------------------------------------------------------------
+%% ImageVersions = ['dropbearv2016.72',
+%%                  'openssh4.4p1-openssl0.9.8c',
+%%                  ...
+%%                  'openssh8.8p1-openssl1.1.1l']
+
+log_image_versions(ImageVersions, Config) ->
+    case true == (catch
+                      lists:member({save_ssh_data,3},
+                                   ssh_collect_labmachine_info_SUITE:module_info(exports)))
+    of
+        true ->
+            HostPfx = hostname()++"_docker",
+            {_Imax, Entries} = lists:foldl(fix_entry(HostPfx), {0,[]}, ImageVersions),
+            ssh_collect_labmachine_info_SUITE:save_ssh_data(HostPfx, Entries, Config);
+        false ->
+            Config
+    end.
+
+
+fix_entry(HostPfx) ->
+    fun(E, {I,Acc}) ->
+            Entry =
+                [{hostname,           lists:flatten(io_lib:format("~s:~2..0w",[HostPfx,I]))},
+                 {type,               compat_test},
+                 {date,               date()},
+                 {time,               time()},
+                 {os_type,            os:type()},
+                 {os_version,         os:version()},
+                 {full_ssh_version,   fix_version(E)}
+                ],
+            {I+1, [Entry|Acc]}
+    end.
+
+fix_version(E) ->
+    case string:tokens(atom_to_list(E), "-") of
+        ["openssh"++Vs, "openssl"++Vc ] -> lists:concat(["OpenSSH_",Vs," OpenSSL ",Vc]);
+        ["openssh"++Vs, "libressl"++Vc] -> lists:concat(["OpenSSH_",Vs," LibreSSL ",Vc]);
+        _ -> atom_to_list(E)
+    end.
+
+hostname() ->
+    case inet:gethostname() of
+	{ok,Name} -> string:to_lower(Name);
+	_ -> "undefined"
+    end.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_cth.erl
===================================================================
--- /dev/null
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_cth.erl
@@ -0,0 +1,104 @@
+-module(ssh_cth).
+
+-export([id/1,
+         init/2,
+         pre_init_per_suite/3,
+         pre_end_per_suite/3,
+         pre_init_per_group/4,
+         post_init_per_group/5,
+         pre_end_per_group/4,
+         post_end_per_group/5,
+         pre_init_per_testcase/4,
+         post_init_per_testcase/5,
+         pre_end_per_testcase/4,
+         post_end_per_testcase/5
+        ]).
+
+-record(c, {
+          known_hosts_file_name = filename:join(os:getenv("HOME"), ".ssh/known_hosts"),
+          known_hosts_last_contents = <<>>,
+          suite, % suite name
+          groups = [], % Group path in reversed order
+          n % test case number
+         }).
+
+id(Opts) ->
+    proplists:get_value(filename, Opts, "/tmp/file.log").
+
+init(_Id, _Opts) ->
+    {ok, #c{n=1}}.
+
+pre_init_per_suite(Suite, Config, State0) ->
+    {_, State} = read_known_hosts_diff(State0#c{suite=Suite}),
+    {Config, State}.
+
+pre_end_per_suite(Suite, Config, State) ->
+    ct:pal("BEGIN ~p:end_per_suite(...)", [Suite]),
+    {Config, State}.
+
+
+pre_init_per_group(Suite, Group, Config, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~sBEGIN ~p:init_per_group(~p,...)", [log_diff(Diff),Suite,Group]),
+    {Config, State#c{groups = (State#c.groups ++ [Group])}}.
+
+post_init_per_group(Suite, Group, _Config, Return, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~sEND ~p:init_per_group(~p,...)", [log_diff(Diff),Suite,Group]),
+    {Return, State}.
+
+pre_end_per_group(Suite, Group, Config, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~sBEGIN ~p:end_per_group(~p,...)", [log_diff(Diff), Suite, Group]),
+    {Config, State}.
+
+post_end_per_group(Suite, Group, _Config, Return, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~sEND ~p:end_per_group(~p,...)", [log_diff(Diff),Suite,Group]),
+    {Return, State#c{groups = lists:reverse(lists:reverse(State#c.groups)--[Group])}}.
+
+
+pre_init_per_testcase(SuiteName, _TC, Config, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~s########## ~p ~p ~s ~p", [log_diff(Diff), State0#c.suite, State0#c.n, groups(Config), SuiteName]),
+    {Config, State#c{n = State#c.n + 1}}.
+
+post_init_per_testcase(SuiteName, TestcaseName, _Config, Return, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~send ~p:init_per_testcase(~p,...)", [log_diff(Diff), SuiteName, TestcaseName]),
+    {Return, State}.
+
+pre_end_per_testcase(Suite, TC, Config, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~sBEGIN ~p:end_per_testcase(~p,...)", [log_diff(Diff), Suite, TC]),
+    {Config, State}.
+
+post_end_per_testcase(SuiteName, TC, _Config, Return, State0) ->
+    {Diff, State} = read_known_hosts_diff(State0),
+    ct:pal("~sEND ~p:end_per_testcase(~p,...)", [log_diff(Diff), SuiteName, TC]),
+    {Return, State}.
+
+
+
+
+groups(Config) ->
+    F = fun(X) -> io_lib:format("~w",[X]) end,
+    io_lib:format("~s", [lists:join("/", lists:map(F,get_groups(Config)))]).
+
+get_groups(Config) ->
+    P = proplists:get_value(tc_group_path, Config, []) ++
+        [proplists:get_value(tc_group_properties, Config, [])],
+    [Name || L <- P,
+             is_list(L),
+             {name,Name} <- L].
+    
+
+
+read_known_hosts_diff(S = #c{known_hosts_file_name = File,
+                             known_hosts_last_contents = Bin0}) ->
+    {ok,  <<Bin0:(size(Bin0))/binary, Diff/binary>> = Bin} = file:read_file(File),
+    {Diff, S#c{known_hosts_last_contents = Bin}}.
+
+log_diff(<<>>) -> "";
+log_diff(Bin) -> io_lib:format("~n++++ ~p~n",[Bin]).
+    
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_engine_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_engine_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_engine_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -80,6 +80,8 @@ init_per_suite(Config) ->
                        {skip, "Engine not supported on this OpenSSL version"};
                    {error, bad_engine_id} ->
                        {skip, "Dynamic Engine not supported"};
+                   {error, notexist} ->
+                       {skip, "No Dynamic Engine to test with"};
                    Other ->
                        ct:log("Engine load failed: ~p",[Other]),
                        {fail, "Engine load failed"}
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_key_cb.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_key_cb.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_key_cb.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2015-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_key_cb_engine_keys.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_key_cb_engine_keys.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_key_cb_engine_keys.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2015-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_key_cb_options.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_key_cb_options.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_key_cb_options.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2015-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_pubkey_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_pubkey_SUITE.erl
@@ -34,36 +34,63 @@
         ]).
 
 -export([
+         check_dsa_disabled/1,
+         check_rsa_sha1_disabled/1,
          connect_dsa_to_dsa/1,
          connect_dsa_to_ecdsa/1,
          connect_dsa_to_ed25519/1,
          connect_dsa_to_ed448/1,
-         connect_dsa_to_rsa/1,
+         connect_dsa_to_rsa_sha2/1,
          connect_ecdsa_to_dsa/1,
          connect_ecdsa_to_ecdsa/1,
          connect_ecdsa_to_ed25519/1,
          connect_ecdsa_to_ed448/1,
-         connect_ecdsa_to_rsa/1,
+         connect_ecdsa_to_rsa_sha2/1,
          connect_ed25519_to_dsa/1,
          connect_ed25519_to_ecdsa/1,
          connect_ed25519_to_ed25519/1,
          connect_ed25519_to_ed448/1,
-         connect_ed25519_to_rsa/1,
+         connect_ed25519_to_rsa_sha2/1,
          connect_ed448_to_dsa/1,
          connect_ed448_to_ecdsa/1,
          connect_ed448_to_ed25519/1,
          connect_ed448_to_ed448/1,
-         connect_ed448_to_rsa/1,
-         connect_rsa_to_dsa/1,
-         connect_rsa_to_ecdsa/1,
-         connect_rsa_to_ed25519/1,
-         connect_rsa_to_ed448/1,
-         connect_rsa_to_rsa/1,
+         connect_ed448_to_rsa_sha2/1,
+         connect_rsa_sha1_to_dsa/1,
+         connect_rsa_sha2_to_dsa/1,
+         connect_rsa_sha2_to_ecdsa/1,
+         connect_rsa_sha2_to_ed25519/1,
+         connect_rsa_sha2_to_ed448/1,
+         connect_rsa_sha2_to_rsa_sha2/1,
+
+         ssh_rsa_public_key/1,
+         ssh_dsa_public_key/1,
+         ssh_ecdsa_public_key/1,
+         ssh_rfc4716_rsa_comment/1,
+         ssh_rfc4716_dsa_comment/1,
+         ssh_rfc4716_rsa_subject/1,
+         ssh_list_public_key/1,
+         ssh_known_hosts/1,
+         ssh1_known_hosts/1,
+         ssh_auth_keys/1,
+         ssh1_auth_keys/1,
+         ssh_openssh_key_with_comment/1,
+         ssh_openssh_key_long_header/1,
+
+         ssh_hostkey_fingerprint_md5_implicit/1,
+         ssh_hostkey_fingerprint_md5/1,
+         ssh_hostkey_fingerprint_sha/1,
+         ssh_hostkey_fingerprint_sha256/1,
+         ssh_hostkey_fingerprint_sha384/1,
+         ssh_hostkey_fingerprint_sha512/1,
+         ssh_hostkey_fingerprint_list/1,
 
-         chk_known_hosts/1
+         chk_known_hosts/1,
+         ssh_hostkey_pkcs8/1
         ]).
 
 -include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
 -include("ssh_test_lib.hrl").
 
 %%%----------------------------------------------------------------
@@ -78,48 +105,72 @@ all() ->
     [{group, old_format},
      {group, new_format},
      {group, option_space},
+     {group, ssh_hostkey_fingerprint},
+     {group, ssh_public_key_decode_encode},
+     {group, pkcs8},
      chk_known_hosts
     ].
 
 
--define(tests_old, [connect_rsa_to_rsa,
-                    connect_rsa_to_dsa,
-                    connect_rsa_to_ecdsa,
-                    connect_dsa_to_rsa,
+-define(tests_old, [connect_rsa_sha2_to_rsa_sha2,
+                    connect_rsa_sha1_to_dsa,
+                    connect_rsa_sha2_to_dsa,
+                    connect_rsa_sha2_to_ecdsa,
+                    connect_dsa_to_rsa_sha2,
                     connect_dsa_to_dsa,
                     connect_dsa_to_ecdsa,
-                    connect_ecdsa_to_rsa,
+                    connect_ecdsa_to_rsa_sha2,
                     connect_ecdsa_to_dsa,
-                    connect_ecdsa_to_ecdsa
-                   ]).
-
--define(tests_new, [
+                    connect_ecdsa_to_ecdsa,
                     connect_dsa_to_ed25519,
-                    connect_dsa_to_ed448,
                     connect_ecdsa_to_ed25519,
+                    connect_rsa_sha2_to_ed25519,
+                    connect_dsa_to_ed448,
                     connect_ecdsa_to_ed448,
-                    connect_ed25519_to_dsa,
+                    connect_rsa_sha2_to_ed448
+                   ]).
+
+-define(tests_new, [connect_ed25519_to_dsa,
                     connect_ed25519_to_ecdsa,
                     connect_ed25519_to_ed448,
                     connect_ed25519_to_ed25519,
-                    connect_ed25519_to_rsa,
+                    connect_ed25519_to_rsa_sha2,
                     connect_ed448_to_dsa,
                     connect_ed448_to_ecdsa,
                     connect_ed448_to_ed25519,
                     connect_ed448_to_ed448,
-                    connect_ed448_to_rsa,
-                    connect_rsa_to_ed25519,
-                    connect_rsa_to_ed448
+                    connect_ed448_to_rsa_sha2
                     | ?tests_old % but taken from the new format directory
                    ]).
 
 groups() ->
     [{new_format,  [], ?tests_new},
-     {old_format,  [], ?tests_old++[{group,passphrase}]},
+     {old_format,  [], [check_dsa_disabled, check_rsa_sha1_disabled | ?tests_old++[{group,passphrase}] ]},
      {passphrase,  [], ?tests_old},
-     {option_space,[], [{group,new_format}]}
+     {option_space,[], [{group,new_format}]},
+     {pkcs8, [], [ssh_hostkey_pkcs8]},
+
+     {ssh_hostkey_fingerprint, [],
+      [ssh_hostkey_fingerprint_md5_implicit,
+       ssh_hostkey_fingerprint_md5,
+       ssh_hostkey_fingerprint_sha,
+       ssh_hostkey_fingerprint_sha256,
+       ssh_hostkey_fingerprint_sha384,
+       ssh_hostkey_fingerprint_sha512,
+       ssh_hostkey_fingerprint_list]},
+
+     {ssh_public_key_decode_encode, [],
+      [ssh_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_public_key,
+       ssh_rfc4716_rsa_comment, ssh_rfc4716_dsa_comment,
+       ssh_rfc4716_rsa_subject,
+       ssh_list_public_key,
+       ssh_known_hosts, %% ssh1_known_hosts,
+       ssh_auth_keys, %% ssh1_auth_keys,
+       ssh_openssh_key_with_comment,
+       ssh_openssh_key_long_header]}
     ].
 
+
 %%%----------------------------------------------------------------
 init_per_suite(Config) ->
     ?CHECK_CRYPTO(
@@ -144,6 +195,11 @@ init_per_group(old_format, Config) ->
     [{fmt,old_format},
      {key_src_dir,Dir} | Config];
 
+init_per_group(pkcs8, Config) ->
+    Dir = filename:join(proplists:get_value(data_dir,Config), "pkcs8"),
+    [{fmt,pkcs8},
+     {key_src_dir,Dir} | Config];
+
 init_per_group(option_space, Config) ->
     extend_optsL([client_opts,daemon_opts],
                  [{key_cb, {ssh_file, [{optimize, space}]}}],
@@ -162,6 +218,12 @@ init_per_group(passphrase, Config0) ->
             {skip, "Unsupported hash"}
     end;
 
+init_per_group(ssh_public_key_decode_encode, Config) ->
+    [{pk_data_dir,
+      filename:join([proplists:get_value(data_dir, Config),
+                     "public_key"])
+     } | Config];
+
 init_per_group(_, Config) ->
     Config.
 
@@ -183,18 +245,22 @@ end_per_group(_, Config) ->
     Config.
 
 %%%----------------------------------------------------------------
-init_per_testcase(connect_rsa_to_rsa, Config0) ->
-    setup_user_system_dir(rsa, rsa, Config0);
-init_per_testcase(connect_rsa_to_dsa, Config0) ->
-    setup_user_system_dir(rsa, dsa, Config0);
-init_per_testcase(connect_rsa_to_ecdsa, Config0) ->
-    setup_user_system_dir(rsa, ecdsa, Config0);
-init_per_testcase(connect_rsa_to_ed25519, Config0) ->
-    setup_user_system_dir(rsa, ed25519, Config0);
-init_per_testcase(connect_rsa_to_ed448, Config0) ->
-    setup_user_system_dir(rsa, ed448, Config0);
-init_per_testcase(connect_dsa_to_rsa, Config0) ->
-    setup_user_system_dir(dsa, rsa, Config0);
+init_per_testcase(ssh_hostkey_pkcs8, Config0) ->
+    setup_user_system_dir(rsa_sha2, rsa_sha2, Config0);
+init_per_testcase(connect_rsa_sha2_to_rsa_sha2, Config0) ->
+    setup_user_system_dir(rsa_sha2, rsa_sha2, Config0);
+init_per_testcase(connect_rsa_sha1_to_dsa, Config0) ->
+    setup_user_system_dir(rsa_sha1, dsa, Config0);
+init_per_testcase(connect_rsa_sha2_to_dsa, Config0) ->
+    setup_user_system_dir(rsa_sha2, dsa, Config0);
+init_per_testcase(connect_rsa_sha2_to_ecdsa, Config0) ->
+    setup_user_system_dir(rsa_sha2, ecdsa, Config0);
+init_per_testcase(connect_rsa_sha2_to_ed25519, Config0) ->
+    setup_user_system_dir(rsa_sha2, ed25519, Config0);
+init_per_testcase(connect_rsa_sha2_to_ed448, Config0) ->
+    setup_user_system_dir(rsa_sha2, ed448, Config0);
+init_per_testcase(connect_dsa_to_rsa_sha2, Config0) ->
+    setup_user_system_dir(dsa, rsa_sha2, Config0);
 init_per_testcase(connect_dsa_to_dsa, Config0) ->
     setup_user_system_dir(dsa, dsa, Config0);
 init_per_testcase(connect_dsa_to_ecdsa, Config0) ->
@@ -203,8 +269,8 @@ init_per_testcase(connect_dsa_to_ed25519
     setup_user_system_dir(dsa, ed25519, Config0);
 init_per_testcase(connect_dsa_to_ed448, Config0) ->
     setup_user_system_dir(dsa, ed448, Config0);
-init_per_testcase(connect_ecdsa_to_rsa, Config0) ->
-    setup_user_system_dir(ecdsa, rsa, Config0);
+init_per_testcase(connect_ecdsa_to_rsa_sha2, Config0) ->
+    setup_user_system_dir(ecdsa, rsa_sha2, Config0);
 init_per_testcase(connect_ecdsa_to_dsa, Config0) ->
     setup_user_system_dir(ecdsa, dsa, Config0);
 init_per_testcase(connect_ecdsa_to_ecdsa, Config0) ->
@@ -213,8 +279,8 @@ init_per_testcase(connect_ecdsa_to_ed255
     setup_user_system_dir(ecdsa, ed25519, Config0);
 init_per_testcase(connect_ecdsa_to_ed448, Config0) ->
     setup_user_system_dir(ecdsa, ed448, Config0);
-init_per_testcase(connect_ed25519_to_rsa, Config0) ->
-    setup_user_system_dir(ed25519, rsa, Config0);
+init_per_testcase(connect_ed25519_to_rsa_sha2, Config0) ->
+    setup_user_system_dir(ed25519, rsa_sha2, Config0);
 init_per_testcase(connect_ed25519_to_dsa, Config0) ->
     setup_user_system_dir(ed25519, dsa, Config0);
 init_per_testcase(connect_ed25519_to_ecdsa, Config0) ->
@@ -223,8 +289,8 @@ init_per_testcase(connect_ed25519_to_ed2
     setup_user_system_dir(ed25519, ed25519, Config0);
 init_per_testcase(connect_ed25519_to_ed448, Config0) ->
     setup_user_system_dir(ed25519, ed448, Config0);
-init_per_testcase(connect_ed448_to_rsa, Config0) ->
-    setup_user_system_dir(ed448, rsa, Config0);
+init_per_testcase(connect_ed448_to_rsa_sha2, Config0) ->
+    setup_user_system_dir(ed448, rsa_sha2, Config0);
 init_per_testcase(connect_ed448_to_dsa, Config0) ->
     setup_user_system_dir(ed448, dsa, Config0);
 init_per_testcase(connect_ed448_to_ecdsa, Config0) ->
@@ -233,31 +299,74 @@ init_per_testcase(connect_ed448_to_ed255
     setup_user_system_dir(ed448, ed25519, Config0);
 init_per_testcase(connect_ed448_to_ed448, Config0) ->
     setup_user_system_dir(ed448, ed448, Config0);
+
+init_per_testcase(check_dsa_disabled, Config0) ->
+    setup_default_user_system_dir(dsa, Config0);
+init_per_testcase(check_rsa_sha1_disabled, Config0) ->
+    setup_default_user_system_dir(rsa_sha1, Config0);
+
+init_per_testcase(ssh_hostkey_fingerprint_md5_implicit, Config) ->
+    init_fingerprint_testcase([md5], Config);
+
+init_per_testcase(ssh_hostkey_fingerprint_md5, Config) ->
+    init_fingerprint_testcase([md5], Config);
+
+init_per_testcase(ssh_hostkey_fingerprint_sha, Config) ->
+    init_fingerprint_testcase([sha], Config);
+
+init_per_testcase(ssh_hostkey_fingerprint_sha256, Config) ->
+    init_fingerprint_testcase([sha256], Config);
+
+init_per_testcase(ssh_hostkey_fingerprint_sha384, Config) ->
+    init_fingerprint_testcase([sha384], Config);
+
+init_per_testcase(ssh_hostkey_fingerprint_sha512, Config) ->
+    init_fingerprint_testcase([sha512], Config);
+
+init_per_testcase(ssh_hostkey_fingerprint_list  , Config) ->
+    init_fingerprint_testcase([sha,md5], Config);
+
 init_per_testcase(_, Config) ->
     Config.
 
+
 end_per_testcase(_, Config) ->
     Config.
 
+%%%----
+init_fingerprint_testcase(Algs, Config0) ->
+    Hashs = proplists:get_value(hashs, crypto:supports(), []),
+    case Algs -- Hashs of
+        [] ->
+            Config = lists:keydelete(watchdog, 1, Config0),
+            Dog = ct:timetrap(?TIMEOUT),
+            [{watchdog, Dog} | Config];
+        UnsupportedAlgs ->
+            {skip,{UnsupportedAlgs,not_supported}}
+    end.
+
 %%%----------------------------------------------------------------
 %%% Test Cases ----------------------------------------------------
 %%%----------------------------------------------------------------
-connect_rsa_to_rsa(Config) ->
+connect_rsa_sha2_to_rsa_sha2(Config) ->
     try_connect(Config).
 
-connect_rsa_to_dsa(Config) ->
+connect_rsa_sha1_to_dsa(Config) ->
     try_connect(Config).
 
-connect_rsa_to_ecdsa(Config) ->
+connect_rsa_sha2_to_dsa(Config) ->
+    try_connect(Config).
+
+connect_rsa_sha2_to_ecdsa(Config) ->
     try_connect(Config). 
 
-connect_rsa_to_ed25519(Config) ->
+connect_rsa_sha2_to_ed25519(Config) ->
     try_connect(Config).
 
-connect_rsa_to_ed448(Config) ->
+connect_rsa_sha2_to_ed448(Config) ->
     try_connect(Config).
 
-connect_dsa_to_rsa(Config) ->
+connect_dsa_to_rsa_sha2(Config) ->
     try_connect(Config).
 
 connect_dsa_to_dsa(Config) ->
@@ -272,7 +381,7 @@ connect_dsa_to_ed25519(Config) ->
 connect_dsa_to_ed448(Config) ->
     try_connect(Config).
 
-connect_ecdsa_to_rsa(Config) ->
+connect_ecdsa_to_rsa_sha2(Config) ->
     try_connect(Config). 
 
 connect_ecdsa_to_dsa(Config) ->
@@ -287,7 +396,7 @@ connect_ecdsa_to_ed25519(Config) ->
 connect_ecdsa_to_ed448(Config) ->
     try_connect(Config).
 
-connect_ed25519_to_rsa(Config) ->
+connect_ed25519_to_rsa_sha2(Config) ->
     try_connect(Config).
 
 connect_ed25519_to_dsa(Config) ->
@@ -302,7 +411,7 @@ connect_ed25519_to_ed25519(Config) ->
 connect_ed25519_to_ed448(Config) ->
     try_connect(Config).
 
-connect_ed448_to_rsa(Config) ->
+connect_ed448_to_rsa_sha2(Config) ->
     try_connect(Config).
 
 connect_ed448_to_dsa(Config) ->
@@ -317,6 +426,382 @@ connect_ed448_to_ed25519(Config) ->
 connect_ed448_to_ed448(Config) ->
     try_connect(Config).
 
+%%%----------------------------------------------------------------
+check_dsa_disabled(Config) ->
+    try_connect_disabled(Config).
+            
+check_rsa_sha1_disabled(Config) ->
+    try_connect_disabled(Config).
+
+
+%%%----------------------------------------------------------------
+
+%% Check of different host keys left to later
+ssh_hostkey_pkcs8(Config) ->
+    try_connect(Config).
+
+%%%----------------------------------------------------------------
+
+%% Check of different host keys left to later
+ssh_hostkey_fingerprint_md5_implicit(_Config) ->
+    Expected = "4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a",
+    Expected = ssh:hostkey_fingerprint(ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Check of different host keys left to later
+ssh_hostkey_fingerprint_md5(_Config) ->
+    Expected = "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a",
+    Expected = ssh:hostkey_fingerprint(md5, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead. The Expected is generated with:
+%%       $ openssh-7.3p1/ssh-keygen -E sha1 -lf <file>
+%%       2048 SHA1:Soammnaqg06jrm2jivMSnzQGlmk none@example.org (RSA)
+ssh_hostkey_fingerprint_sha(_Config) ->
+    Expected = "SHA1:Soammnaqg06jrm2jivMSnzQGlmk",
+    Expected = ssh:hostkey_fingerprint(sha, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_sha256(_Config) ->
+    Expected = "SHA256:T7F1BahkJWR7iJO8+rpzWOPbp7LZP4MlNrDExdNYOvY",
+    Expected = ssh:hostkey_fingerprint(sha256, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_sha384(_Config) ->
+    Expected = "SHA384:QhkLoGNI4KXdPvC//HxxSCP3uTQVADqxdajbgm+Gkx9zqz8N94HyP1JmH8C4/aEl",
+    Expected = ssh:hostkey_fingerprint(sha384, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_sha512(_Config) ->
+    Expected = "SHA512:ezUismvm3ADQQb6Nm0c1DwQ6ydInlJNfsnSQejFkXNmABg1Aenk9oi45CXeBOoTnlfTsGG8nFDm0smP10PBEeA",
+    Expected = ssh:hostkey_fingerprint(sha512, ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+%% Since this kind of fingerprint is not available yet on standard
+%% distros, we do like this instead.
+ssh_hostkey_fingerprint_list(_Config) ->
+    Expected = ["SHA1:Soammnaqg06jrm2jivMSnzQGlmk",
+                "MD5:4b:0b:63:de:0f:a7:3a:ab:2c:cc:2d:d1:21:37:1d:3a"],
+    Expected = ssh:hostkey_fingerprint([sha,md5], ssh_hostkey(rsa)).
+
+%%--------------------------------------------------------------------
+ssh_rsa_public_key(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+    {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")),
+    [{PubKey, Attributes1}] = ssh_file:decode(RSARawSsh2, public_key),
+    [{PubKey, Attributes1}] = ssh_file:decode(RSARawSsh2, rfc4716_key),
+
+    {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")),
+    [{PubKey, Attributes2}] = ssh_file:decode(RSARawOpenSsh, public_key),
+    [{PubKey, Attributes2}] = ssh_file:decode(RSARawOpenSsh, openssh_key),
+
+    %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh
+    %% = RSARawOpenSsh as line breakpoints may differ
+
+    EncodedSSh = ssh_file:encode([{PubKey, Attributes1}], rfc4716_key),
+    EncodedOpenSsh = ssh_file:encode([{PubKey, Attributes2}], openssh_key),
+
+    [{PubKey, Attributes1}] =
+	ssh_file:decode(EncodedSSh, public_key),
+    [{PubKey, Attributes2}] =
+	ssh_file:decode(EncodedOpenSsh, public_key).
+
+%%--------------------------------------------------------------------
+ssh_dsa_public_key(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")),
+    [{PubKey, Attributes1}] = ssh_file:decode(DSARawSsh2, public_key),
+    [{PubKey, Attributes1}] = ssh_file:decode(DSARawSsh2, rfc4716_key),
+
+    {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")),
+    [{PubKey, Attributes2}] = ssh_file:decode(DSARawOpenSsh, public_key),
+    [{PubKey, Attributes2}] = ssh_file:decode(DSARawOpenSsh, openssh_key),
+
+    %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh
+    %% = DSARawOpenSsh as line breakpoints may differ
+
+    EncodedSSh = ssh_file:encode([{PubKey, Attributes1}], rfc4716_key),
+    EncodedOpenSsh = ssh_file:encode([{PubKey, Attributes2}], openssh_key),
+
+    [{PubKey, Attributes1}] =
+	ssh_file:decode(EncodedSSh, public_key),
+    [{PubKey, Attributes2}] =
+	ssh_file:decode(EncodedOpenSsh, public_key).
+
+%%--------------------------------------------------------------------
+ssh_ecdsa_public_key(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, ECDSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_ecdsa_pub")),
+    [{PubKey, Attributes1}] = ssh_file:decode(ECDSARawSsh2, public_key),
+    [{PubKey, Attributes1}] = ssh_file:decode(ECDSARawSsh2, rfc4716_key),
+
+    {ok, ECDSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_ecdsa_pub")),
+    [{PubKey, Attributes2}] = ssh_file:decode(ECDSARawOpenSsh, public_key),
+    [{PubKey, Attributes2}] =ssh_file:decode(ECDSARawOpenSsh, openssh_key),
+
+    %% Can not check EncodedSSh == ECDSARawSsh2 and EncodedOpenSsh
+    %% = ECDSARawOpenSsh as line breakpoints may differ
+
+    EncodedSSh = ssh_file:encode([{PubKey, Attributes1}], rfc4716_key),
+    EncodedOpenSsh = ssh_file:encode([{PubKey, Attributes2}], openssh_key),
+
+    [{PubKey, Attributes1}] =
+	ssh_file:decode(EncodedSSh, public_key),
+    [{PubKey, Attributes2}] =
+	ssh_file:decode(EncodedOpenSsh, public_key).
+
+%%--------------------------------------------------------------------
+ssh_list_public_key(Config) when is_list(Config) ->
+    DataDir = proplists:get_value(pk_data_dir, Config),
+    {Data_ssh2, Expect_ssh2} =
+        collect_binaries_expected(DataDir, rfc4716_key,
+                                  ["ssh2_rsa_pub", "ssh2_rsa_comment_pub",
+                                   "ssh2_dsa_pub", "ssh2_dsa_comment_pub",
+                                   "ssh2_ecdsa_pub", 
+                                   "ssh2_subject_pub"]),
+    {Data_openssh, Expect_openssh} =
+        collect_binaries_expected(DataDir, openssh_key,
+                                  ["openssh_rsa_pub", "openssh_dsa_pub", "openssh_ecdsa_pub"]),
+
+    true =
+        (chk_decode(Data_openssh,   Expect_openssh, openssh_key) and
+         chk_decode(Data_ssh2,      Expect_ssh2,    rfc4716_key) and
+         chk_decode(Data_openssh,   Expect_openssh, public_key)         and
+         chk_decode(Data_ssh2,      Expect_ssh2,    public_key)         and
+         chk_encode(Expect_openssh, openssh_key) and
+         chk_encode(Expect_ssh2,    rfc4716_key)
+        ).
+
+chk_encode(Data, Type) ->
+    case ssh_file:decode(ssh_file:encode(Data,Type), Type) of
+        Data->
+            ct:log("re-encode ~p ok", [Type]),
+            true;
+        Result ->
+            ct:log("re-encode ~p FAILED~n"
+                   "Got~n ~p~nExpect~n ~p~n",
+                   [Type, Result, Data]),
+            false
+    end.
+
+
+chk_decode(Data, Expect, Type) ->
+    case ssh_file:decode(Data, Type) of
+        Expect ->
+            ct:log("decode ~p ok", [Type]),
+            true;
+        BadResult ->
+            ct:log("decode ~p FAILED~n"
+                   "Result~n ~p~nExpect~n ~p~n"
+                   "~p",
+                   [Type, BadResult, Expect,
+                    if
+                        is_list(BadResult) ->
+                            lists:foldr(fun({Key,Attrs}, Acc) ->
+                                                case Key of
+                                                    #'RSAPublicKey'{} when is_list(Attrs) -> Acc;
+                                                    {_, #'Dss-Parms'{}} when is_list(Attrs) -> Acc;
+                                                    {#'ECPoint'{}, {namedCurve,_}} when is_list(Attrs) -> Acc;
+                                                    _  when is_list(Attrs) -> [{bad_key,{Key,Attrs}}|Acc];
+                                                    _ -> [{bad_attrs,{Key,Attrs}}|Acc]
+                                                end;
+                                           (Other,Acc) ->
+                                                [{other,Other}|Acc]
+                                        end, [], BadResult);
+                        true ->
+                            '???'
+                    end]),
+            false
+    end.
+
+
+collect_binaries_expected(Dir, Type, Files) ->
+    Bins0 = [B || F <- Files,
+                  {ok,B} <- [ file:read_file(filename:join(Dir,F)) ]
+            ],
+    {list_to_binary( lists:join("\n", Bins0)),
+     lists:flatten([ssh_file:decode(B,Type) || B <- Bins0])}.
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_rsa_comment(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")),
+    [{#'RSAPublicKey'{} = PubKey, Attributes}] =
+        ssh_file:decode(RSARawSsh2, public_key),
+
+    Headers = proplists:get_value(headers, Attributes),
+
+    Value = proplists:get_value("Comment", Headers, undefined),
+    true = Value =/= undefined,
+    RSARawSsh2 = ssh_file:encode([{PubKey, Attributes}], rfc4716_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_dsa_comment(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")),
+    [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] =
+        ssh_file:decode(DSARawSsh2, public_key),
+
+    Headers = proplists:get_value(headers, Attributes),
+
+    Value = proplists:get_value("Comment", Headers, undefined),
+    true = Value =/= undefined,
+
+    %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ
+    Encoded  = ssh_file:encode([{PubKey, Attributes}], rfc4716_key),
+    [{PubKey, Attributes}] =
+        ssh_file:decode(Encoded, public_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_rsa_subject(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")),
+    [{#'RSAPublicKey'{} = PubKey, Attributes}] =
+        ssh_file:decode(RSARawSsh2, public_key),
+
+    Headers = proplists:get_value(headers, Attributes),
+
+    Value = proplists:get_value("Subject", Headers, undefined),
+    true = Value =/= undefined,
+
+    %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ
+    Encoded  = ssh_file:encode([{PubKey, Attributes}], rfc4716_key),
+    [{PubKey, Attributes}] =
+        ssh_file:decode(Encoded, public_key).
+
+%%--------------------------------------------------------------------
+ssh_known_hosts(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")),
+    [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},
+     {#'RSAPublicKey'{}, Attributes3}, {#'RSAPublicKey'{}, Attributes4}] = Decoded =
+        ssh_file:decode(SshKnownHosts, known_hosts),
+
+    Comment1 = undefined,
+    Comment2 = "foo@bar.com",
+    Comment3 = "Comment with whitespaces",
+    Comment4 = "foo@bar.com Comment with whitespaces",
+    	
+    Comment1 = proplists:get_value(comment, Attributes1, undefined),
+    Comment2 = proplists:get_value(comment, Attributes2),
+    Comment3 = proplists:get_value(comment, Attributes3),
+    Comment4 = proplists:get_value(comment, Attributes4),	
+
+    Value1 = proplists:get_value(hostnames, Attributes1, undefined),
+    Value2 = proplists:get_value(hostnames, Attributes2, undefined),
+    true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+    Encoded = ssh_file:encode(Decoded, known_hosts),
+    Decoded = ssh_file:decode(Encoded, known_hosts).
+
+%%--------------------------------------------------------------------
+ssh1_known_hosts(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")),
+    [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},{#'RSAPublicKey'{}, Attributes3}] 
+	= Decoded = ssh_file:decode(SshKnownHosts, known_hosts),
+
+    Value1 = proplists:get_value(hostnames, Attributes1, undefined),
+    Value2 = proplists:get_value(hostnames, Attributes2, undefined),
+    true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+    Comment ="dhopson@VMUbuntu-DSH comment with whitespaces",
+    Comment = proplists:get_value(comment, Attributes3),
+
+    Encoded = ssh_file:encode(Decoded, known_hosts),
+    Decoded = ssh_file:decode(Encoded, known_hosts).
+
+%%--------------------------------------------------------------------
+ssh_auth_keys(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")),
+    [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, Attributes2},
+     {#'RSAPublicKey'{}, Attributes3}, {{_, #'Dss-Parms'{}}, Attributes4}
+    ] = Decoded =
+        ssh_file:decode(SshAuthKeys, auth_keys),
+
+    Value1 = proplists:get_value(options, Attributes1, undefined),
+    true = Value1 =/= undefined,
+
+    Comment1 = Comment2 = "dhopson@VMUbuntu-DSH",
+    Comment3 = Comment4 ="dhopson@VMUbuntu-DSH comment with whitespaces",
+    
+    Comment1 = proplists:get_value(comment, Attributes1),
+    Comment2 = proplists:get_value(comment, Attributes2),
+    Comment3 = proplists:get_value(comment, Attributes3),
+    Comment4 = proplists:get_value(comment, Attributes4),
+
+    Encoded = ssh_file:encode(Decoded, auth_keys),
+    Decoded = ssh_file:decode(Encoded, auth_keys).
+
+%%--------------------------------------------------------------------
+ssh1_auth_keys(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")),
+    [{#'RSAPublicKey'{}, Attributes1},
+     {#'RSAPublicKey'{}, Attributes2}, {#'RSAPublicKey'{}, Attributes3},
+     {#'RSAPublicKey'{}, Attributes4}, {#'RSAPublicKey'{}, Attributes5}] = Decoded =
+        ssh_file:decode(SshAuthKeys, auth_keys),
+
+    Value1 = proplists:get_value(bits, Attributes2, undefined),
+    Value2 = proplists:get_value(bits, Attributes3, undefined),
+    true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+    Comment2 = Comment3 = "dhopson@VMUbuntu-DSH",
+    Comment4 = Comment5 ="dhopson@VMUbuntu-DSH comment with whitespaces",
+    
+    undefined = proplists:get_value(comment, Attributes1, undefined),
+    Comment2 = proplists:get_value(comment, Attributes2),
+    Comment3 = proplists:get_value(comment, Attributes3),
+    Comment4 = proplists:get_value(comment, Attributes4),
+    Comment5 = proplists:get_value(comment, Attributes5),
+
+    Encoded = ssh_file:encode(Decoded, auth_keys),
+    Decoded = ssh_file:decode(Encoded, auth_keys).
+
+%%--------------------------------------------------------------------
+ssh_openssh_key_with_comment(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")),
+    [{{_, #'Dss-Parms'{}}, _}] = ssh_file:decode(DSARawOpenSsh, openssh_key).
+
+%%--------------------------------------------------------------------
+ssh_openssh_key_long_header(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(pk_data_dir, Config),
+
+    {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")),
+    [{#'RSAPublicKey'{}, _}] = Decoded = ssh_file:decode(RSARawOpenSsh, public_key),
+
+    Encoded = ssh_file:encode(Decoded, rfc4716_key),
+    Decoded = ssh_file:decode(Encoded, rfc4716_key).
+
+%%%----------------------------------------------------------------
+%%% Test case helpers
+%%%----------------------------------------------------------------
+%% Should use stored keys instead
+ssh_hostkey(rsa) ->
+    [{PKdecoded,_}] =
+	ssh_file:decode(
+	  <<"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYXcYmsyJBstl4EfFYzfQJmSiUE162zvSGSoMYybShYOI6rnnyvvihfw8Aml+2gZ716F2tqG48FQ/yPZEGWNPMrCejPpJctaPWhpNdNMJ8KFXSEgr5bY2mEpa19DHmuDeXKzeJJ+X7s3fVdYc4FMk5731KIW6Huf019ZnTxbx0VKG6b1KAJBg3vpNsDxEMwQ4LFMB0JHVklOTzbxmpaeULuIxvl65A+eGeFVeo2Q+YI9UnwY1vSgmc9Azwy8Ie9Z0HpQBN5I7Uc5xnknT8V6xDhgNfXEfzsgsRdDfZLECt1WO/1gP9wkosvAGZWt5oG8pbNQWiQdFq536ck8WQD9WD none@example.org">>,
+	  public_key),
+    PKdecoded.
 
 %%%----------------------------------------------------------------
 chk_known_hosts(Config) ->
@@ -367,14 +852,15 @@ chk_known_hosts(Config) ->
 
 
 %%%----------------------------------------------------------------
-try_connect({skip,Reson}) ->
-    {skip,Reson};
+try_connect({skip,Reason}) ->
+    {skip,Reason};
 try_connect(Config) ->
     SystemDir = proplists:get_value(system_dir, Config),
     UserDir = proplists:get_value(user_dir, Config),
     ClientOpts = proplists:get_value(client_opts, Config, []),
     DaemonOpts = proplists:get_value(daemon_opts, Config, []),
 
+    ssh_dbg:start(fun ct:log/2), ssh_dbg:on([alg]),
     {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
 					     {user_dir, UserDir}
                                              | DaemonOpts]),
@@ -384,46 +870,29 @@ try_connect(Config) ->
                                           {user_interaction, false}
                                           | ClientOpts]),
     ssh:close(C),
+    ssh_dbg:stop(),
     ssh:stop_daemon(Pid).
 
+
+try_connect_disabled(Config) ->
+    try try_connect(Config)
+    of _ -> {fail, "non-default algorithm accepted"}
+    catch error:{badmatch,{error,"Service not available"}} -> ok
+    end.
+
 %%%----------------------------------------------------------------
 %%% Local ---------------------------------------------------------
 %%%----------------------------------------------------------------
 setup_user_system_dir(ClientAlg, ServerAlg, Config) ->
-    case supported(public_keys, ClientAlg) andalso supported(public_keys, ServerAlg) of
+    case supported(public_key, ClientAlg) andalso supported(public_key, ServerAlg) of
         true ->
-            PrivDir = proplists:get_value(priv_dir, Config),
-            KeySrcDir = proplists:get_value(key_src_dir, Config),
-            Fmt = proplists:get_value(fmt, Config),
-
-            System = lists:concat(["system_", ClientAlg, "_", ServerAlg, "_", Fmt]),
-            SystemDir = filename:join(PrivDir, System),
-            file:make_dir(SystemDir),
-
-            User   = lists:concat(["user_", ClientAlg, "_", ServerAlg, "_", Fmt]),
-            UserDir   = filename:join(PrivDir, User),
-            file:make_dir(UserDir),
-
-            HostSrcFile = filename:join(KeySrcDir, file(host,ServerAlg)),
-            HostDstFile = filename:join(SystemDir, file(host,ServerAlg)),
-
-            UserSrcFile = filename:join(KeySrcDir, file(user,ClientAlg)),
-            UserDstFile = filename:join(UserDir, file(user,ClientAlg)),
-
-            UserPubSrcFile = filename:join(KeySrcDir, file(user,ClientAlg)++".pub"),
-            AuthorizedKeys = filename:join(UserDir, "authorized_keys"),
-
             try
-                {ok,_} = file:copy(UserSrcFile, UserDstFile),
-                {ok,_} = file:copy(UserPubSrcFile, AuthorizedKeys),
-                {ok,_} = file:copy(HostSrcFile, HostDstFile)
+                setup_dirs(ClientAlg, ServerAlg, Config)
             of
-                _ ->
-                    ModAlgs = [{modify_algorithms,
-                                [{append,[{public_key,
-                                           lists:usort([alg(ClientAlg),
-                                                        alg(ServerAlg)])}]}]}
-                              ],
+                {ok, {SystemDir,UserDir}} ->
+                    ModAlgs = [{preferred_algorithms, 
+                                [{public_key, lists:usort([alg(ClientAlg), alg(ServerAlg)])}]
+                               }],
                     [{system_dir,SystemDir},
                      {user_dir,UserDir}
                      | extend_optsL([daemon_opts,client_opts], ModAlgs, Config)]
@@ -437,38 +906,120 @@ setup_user_system_dir(ClientAlg, ServerA
             {skip, unsupported_algorithm}
     end.
 
-%%%----------------------------------------------------------------
-file(host, dsa)     -> "ssh_host_dsa_key";
-file(host, ecdsa)   -> "ssh_host_ecdsa_key";
-file(host, ed25519) -> "ssh_host_ed25519_key";
-file(host, ed448)   -> "ssh_host_ed448_key";
-file(host, rsa)     -> "ssh_host_rsa_key";
-file(user, dsa)     -> "id_dsa";
-file(user, ecdsa)   -> "id_ecdsa";
-file(user, ed25519) -> "id_ed25519";
-file(user, ed448)   -> "id_ed448";
-file(user, rsa)     -> "id_rsa".
+
+setup_default_user_system_dir(ClientAlg, Config) ->
+    ServerAlg = ecdsa,
+    case default(public_key, ClientAlg) of
+        false ->
+            case supported(public_key, ClientAlg) of
+                true ->
+                    case supported(public_key, ServerAlg) of
+                        true ->
+                            try
+                                setup_dirs(ClientAlg, ServerAlg, Config)
+                            of
+                                {ok, {SystemDir,UserDir}} ->
+                                    ModAlgs = [{modify_algorithms,
+                                                [{append,[{public_key,[alg(ServerAlg)]}]},
+                                                 {rm, [{public_key,[alg(ClientAlg)|inv_algs(ClientAlg)]}]}
+                                                ]}],
+                                    [{system_dir,SystemDir},
+                                     {user_dir,UserDir}
+                                     | extend_optsL([daemon_opts,client_opts], ModAlgs, Config)]
+                            catch
+                                error:{badmatch,{error,enoent}}:S ->
+                                    ct:log("~p:~p Stack:~n~p", [?MODULE,?LINE,S]),
+                                    {skip, no_key_file_found}
+                            end;
+                        false ->
+                            {skip, unsupported_server_algorithm}
+                    end;
+                false ->
+                    {skip, unsupported_client_algorithm}
+            end;
+        true ->
+            {fail, disabled_algorithm_present}
+    end.
+            
+            
+setup_dirs(ClientAlg, ServerAlg, Config) ->
+    PrivDir = proplists:get_value(priv_dir, Config),
+    KeySrcDir = proplists:get_value(key_src_dir, Config),
+    Fmt = proplists:get_value(fmt, Config),
+
+    System = lists:concat(["system_", ClientAlg, "_", ServerAlg, "_", Fmt]),
+    SystemDir = filename:join(PrivDir, System),
+    file:make_dir(SystemDir),
+
+    User   = lists:concat(["user_", ClientAlg, "_", ServerAlg, "_", Fmt]),
+    UserDir   = filename:join(PrivDir, User),
+    file:make_dir(UserDir),
+
+    HostSrcFile = filename:join(KeySrcDir, file(src,host,ServerAlg)),
+    HostDstFile = filename:join(SystemDir, file(dst,host,ServerAlg)),
+
+    UserSrcFile = filename:join(KeySrcDir, file(src,user,ClientAlg)),
+    UserDstFile = filename:join(UserDir, file(dst,user,ClientAlg)),
+
+    UserPubSrcFile = filename:join(KeySrcDir, file(src,user,ClientAlg)++".pub"),
+    AuthorizedKeys = filename:join(UserDir, "authorized_keys"),
+
+    ct:log("UserSrcFile = ~p~nUserDstFile = ~p", [UserSrcFile, UserDstFile]),
+    {ok,_} = file:copy(UserSrcFile, UserDstFile),
+    ct:log("UserPubSrcFile = ~p~nAuthorizedKeys = ~p", [UserPubSrcFile, AuthorizedKeys]),
+    {ok,_} = file:copy(UserPubSrcFile, AuthorizedKeys),
+    ct:log("HostSrcFile = ~p~nHostDstFile = ~p", [HostSrcFile, HostDstFile]),
+    {ok,_} = file:copy(HostSrcFile, HostDstFile),
+    
+    ct:log("SystemDir = ~p~nUserDir = ~p", [SystemDir,UserDir]),
+    {ok, {SystemDir,UserDir}}.
+
+%%%----------------------------------------------------------------
+file(  _, host, dsa)     -> "ssh_host_dsa_key";
+file(  _, host, ecdsa)   -> "ssh_host_ecdsa_key";
+file(  _, host, ed25519) -> "ssh_host_ed25519_key";
+file(  _, host, ed448)   -> "ssh_host_ed448_key";
+file(  _, host, rsa_sha2)-> "ssh_host_rsa_key";
+file(src, host, rsa_sha1)-> "ssh_host_rsa_key";
+file(dst, host, rsa_sha1)-> "ssh_host_rsa_key";
+file(  _, user, dsa)     -> "id_dsa";
+file(  _, user, ecdsa)   -> "id_ecdsa";
+file(  _, user, ed25519) -> "id_ed25519";
+file(  _, user, ed448)   -> "id_ed448";
+file(  _, user, rsa_sha2)-> "id_rsa";
+file(src, user, rsa_sha1)-> "id_rsa";
+file(dst, user, rsa_sha1)-> "id_rsa".
 
 alg(dsa)     -> 'ssh-dss';
 alg(ecdsa)   -> 'ecdsa-sha2-nistp256';
 alg(ed25519) -> 'ssh-ed25519';
 alg(ed448)   -> 'ssh-ed448';
-alg(rsa)     -> 'ssh-rsa'.
+alg(rsa_sha2)-> 'rsa-sha2-256';
+alg(rsa_sha1)-> 'ssh-rsa'.
 
+inv_algs(rsa_sha1) -> algs(rsa_sha2);
+inv_algs(_) -> [].
 
-supported(public_keys, rsa) ->     supported(public_key, 'ssh-rsa') orelse
-                                       supported(public_key, 'rsa-sha2-256') orelse
-                                       supported(public_key, 'rsa-sha2-521');
-supported(public_keys, dsa) ->     supported(public_key, 'ssh-dss');
-supported(public_keys, ecdsa) ->   supported(public_key, 'ecdsa-sha2-nistp256') orelse
-                                       supported(public_key, 'ecdsa-sha2-nistp384') orelse
-                                       supported(public_key, 'ecdsa-sha2-nistp521');
-supported(public_keys, ed448) ->   supported(public_key, 'ssh-ed448');
-supported(public_keys, ed25519) -> supported(public_key, 'ssh-ed25519');
-supported(Type, Alg) ->
-    case proplists:get_value(Type,ssh_transport:supported_algorithms()) of
-        undefined ->
-            lists:member(Alg, crypto:supports(Type));
-        L ->
-            lists:member(Alg, L)
-    end.
+algs(dsa)     -> ['ssh-dss'];
+algs(ecdsa)   -> ['ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-521'];
+algs(ed25519) -> ['ssh-ed25519'];
+algs(ed448)   -> ['ssh-ed448'];
+algs(rsa_sha2)-> ['rsa-sha2-256', 'rsa-sha2-384', 'rsa-sha2-512'];
+algs(rsa_sha1)-> ['ssh-rsa'];
+algs(A) -> [A].
+
+
+
+default(Type, Alg) -> listed(algs(Alg), ssh_transport:default_algorithms(Type)).
+
+supported(Type, Alg) -> listed(algs(Alg),
+                               try
+                                   ssh_transport:supported_algorithms(Type)
+                               catch
+                                   error:function_clause -> crypto:supports(Type)
+                               end).
+
+listed(As, L) -> lists:any(fun(A) -> lists:member(A,L) end,
+                           As).
+                                   
+    
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_relay.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_relay.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_relay.erl
@@ -313,7 +313,7 @@ listen(Parent, LSock) ->
     do_listen(Parent, LSock).
 
 do_listen(Parent, LSock) ->
-    %% So annoying there is no select-like sematic for this
+    %% So annoying there is no select-like semantics for this
     case gen_tcp:accept(LSock, ?ACCEPT_TMO) of
 	{ok, Sock} ->
 	    Parent ! {accept, Sock},
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2007-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2021. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_test_lib.hrl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_test_lib.hrl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_test_lib.hrl
@@ -1,10 +1,18 @@
+
+%%-------------------------------------------------------------------------
+%% Default system port
+%%-------------------------------------------------------------------------
+-ifndef(SSH_DEFAULT_PORT).
+-define(SSH_DEFAULT_PORT, 22).
+-endif.
+
 %%-------------------------------------------------------------------------
 %% Timeout time in ms
 %%-------------------------------------------------------------------------
 -define(TIMEOUT, 27000).
 
 %%-------------------------------------------------------------------------
-%% Check for usable crypt 
+%% Check for usable crypto
 %%-------------------------------------------------------------------------
 -define(CHECK_CRYPTO(UsersInitCode),
 	try
@@ -24,35 +32,35 @@
 %%-------------------------------------------------------------------------
 -define(wait_match(Pattern, Guard, FunctionCall, Bind, Timeout, Ntries),
 	Bind =
-	    (fun() -> 
+	    (fun() ->
 		     F = fun(N, F1) ->
 				 case FunctionCall of
 				     Pattern when Guard -> Bind;
 				     _ when N>0 ->
-					 ct:log("Must sleep ~p ms at ~p:~p",[Timeout,?MODULE,?LINE]),
+					 ct:log("Must sleep ~p ms at ~p:~p",
+                                                [Timeout,?MODULE,?LINE]),
 					 timer:sleep(Timeout),
 					 F1(N-1, F1);
-				     Other ->  
-					 ct:fail("Unexpected ~p:~p  ~p",[?MODULE,?LINE,Other])
+				     Other ->
+					 ct:fail("Unexpected ~p:~p  ~p",
+                                                 [?MODULE,?LINE,Other])
 				 end
 			 end,
 		     F(Ntries, F)
 	     end)()
        ).
-
 -define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
         ?wait_match(Pattern, true, FunctionCall, Bind, Timeout, Ntries)).
-
--define(wait_match(Pattern, FunctionCall, Timeout, Ntries),  ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
-
--define(wait_match(Pattern, FunctionCall, Bind),  ?wait_match(Pattern, FunctionCall, Bind, 500, 10) ).
-
--define(wait_match(Pattern, FunctionCall),  ?wait_match(Pattern, FunctionCall, ok) ).
+-define(wait_match(Pattern, FunctionCall, Timeout, Ntries),
+        ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
+-define(wait_match(Pattern, FunctionCall, Bind),
+        ?wait_match(Pattern, FunctionCall, Bind, 500, 10)).
+-define(wait_match(Pattern, FunctionCall),
+        ?wait_match(Pattern, FunctionCall, ok)).
 
 %%-------------------------------------------------------------------------
 %% Write file into log
 %%-------------------------------------------------------------------------
-
 -define(ct_log_show_file(File),
         (fun(File__) ->
                 {ok,Contents__} = file:read_file(File__),
Index: otp-OTP-23.3.4.19/lib/ssh/test/ssh_upgrade_SUITE.erl
===================================================================
--- otp-OTP-23.3.4.19.orig/lib/ssh/test/ssh_upgrade_SUITE.erl
+++ otp-OTP-23.3.4.19/lib/ssh/test/ssh_upgrade_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2014-2020. All Rights Reserved.
+%% Copyright Ericsson AB 2014-2022. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -98,7 +98,7 @@ minor_upgrade(Config) when is_list(Confi
 %%% 
 
 %%%----------------------------------------------------------------
-%%% Initialyze system before upgrade test starts.
+%%% Initialize system before upgrade test starts.
 %%% Called by ct_release_test:upgrade/4
 upgrade_init(CTData, State) -> 
     {ok, AppUp={_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssh),
openSUSE Build Service is sponsored by