asyncssh 2.21.0__tar.gz → 2.22.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. {asyncssh-2.21.0 → asyncssh-2.22.0}/.github/workflows/run_tests.yml +27 -20
  2. {asyncssh-2.21.0/asyncssh.egg-info → asyncssh-2.22.0}/PKG-INFO +16 -25
  3. {asyncssh-2.21.0 → asyncssh-2.22.0}/README.rst +12 -20
  4. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/channel.py +6 -4
  5. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/client.py +1 -1
  6. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/config.py +1 -1
  7. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/connection.py +55 -24
  8. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/__init__.py +7 -7
  9. asyncssh-2.22.0/asyncssh/crypto/chacha.py +103 -0
  10. asyncssh-2.22.0/asyncssh/crypto/ed.py +186 -0
  11. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/forward.py +10 -4
  12. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/misc.py +3 -3
  13. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/process.py +1 -1
  14. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/public_key.py +147 -97
  15. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/scp.py +8 -3
  16. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/sftp.py +18 -8
  17. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/sk.py +23 -12
  18. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/version.py +2 -2
  19. {asyncssh-2.21.0 → asyncssh-2.22.0/asyncssh.egg-info}/PKG-INFO +16 -25
  20. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh.egg-info/requires.txt +1 -1
  21. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/changes.rst +56 -0
  22. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/callback_client.py +2 -2
  23. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/callback_client2.py +2 -2
  24. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/callback_client3.py +2 -2
  25. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/callback_math_server.py +2 -2
  26. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/chat_server.py +2 -2
  27. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/check_exit_status.py +2 -2
  28. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/chroot_sftp_server.py +2 -2
  29. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/direct_client.py +2 -2
  30. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/direct_server.py +2 -2
  31. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/editor.py +2 -2
  32. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/gather_results.py +8 -6
  33. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/listening_client.py +2 -2
  34. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/local_forwarding_client.py +2 -2
  35. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/local_forwarding_client2.py +2 -2
  36. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/local_forwarding_server.py +2 -2
  37. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/math_client.py +2 -2
  38. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/math_server.py +2 -2
  39. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/redirect_input.py +2 -2
  40. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/redirect_local_pipe.py +2 -2
  41. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/redirect_remote_pipe.py +2 -2
  42. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/redirect_server.py +2 -2
  43. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/remote_forwarding_client.py +2 -2
  44. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/remote_forwarding_client2.py +2 -2
  45. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/remote_forwarding_server.py +2 -2
  46. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/reverse_client.py +2 -2
  47. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/reverse_server.py +2 -2
  48. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/scp_client.py +2 -2
  49. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/set_environment.py +2 -2
  50. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/set_terminal.py +2 -2
  51. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/sftp_client.py +2 -2
  52. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/show_environment.py +2 -2
  53. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/show_terminal.py +2 -2
  54. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/simple_cert_server.py +2 -2
  55. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/simple_client.py +2 -2
  56. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/simple_keyed_server.py +2 -2
  57. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/simple_scp_server.py +2 -2
  58. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/simple_server.py +2 -2
  59. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/simple_sftp_server.py +2 -2
  60. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/stream_direct_client.py +2 -2
  61. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/stream_direct_server.py +2 -2
  62. {asyncssh-2.21.0 → asyncssh-2.22.0}/examples/stream_listening_client.py +2 -2
  63. {asyncssh-2.21.0 → asyncssh-2.22.0}/pyproject.toml +3 -4
  64. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/sk_stub.py +22 -6
  65. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_agent.py +3 -4
  66. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_channel.py +27 -1
  67. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_config.py +12 -0
  68. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_connection.py +23 -8
  69. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_connection_auth.py +5 -5
  70. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_forward.py +1 -1
  71. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_kex.py +0 -5
  72. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_pkcs11.py +1 -1
  73. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_public_key.py +5 -16
  74. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_sftp.py +25 -2
  75. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_sk.py +1 -1
  76. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_stream.py +1 -1
  77. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_tuntap.py +1 -1
  78. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/util.py +1 -1
  79. {asyncssh-2.21.0 → asyncssh-2.22.0}/tox.ini +7 -5
  80. asyncssh-2.21.0/asyncssh/crypto/chacha.py +0 -162
  81. asyncssh-2.21.0/asyncssh/crypto/ed.py +0 -325
  82. {asyncssh-2.21.0 → asyncssh-2.22.0}/.coveragerc +0 -0
  83. {asyncssh-2.21.0 → asyncssh-2.22.0}/.gitignore +0 -0
  84. {asyncssh-2.21.0 → asyncssh-2.22.0}/.readthedocs.yaml +0 -0
  85. {asyncssh-2.21.0 → asyncssh-2.22.0}/CONTRIBUTING.rst +0 -0
  86. {asyncssh-2.21.0 → asyncssh-2.22.0}/COPYRIGHT +0 -0
  87. {asyncssh-2.21.0 → asyncssh-2.22.0}/LICENSE +0 -0
  88. {asyncssh-2.21.0 → asyncssh-2.22.0}/MANIFEST.in +0 -0
  89. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/__init__.py +0 -0
  90. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/agent.py +0 -0
  91. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/agent_unix.py +0 -0
  92. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/agent_win32.py +0 -0
  93. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/asn1.py +0 -0
  94. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/auth.py +0 -0
  95. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/auth_keys.py +0 -0
  96. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/compression.py +0 -0
  97. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/constants.py +0 -0
  98. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/cipher.py +0 -0
  99. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/dh.py +0 -0
  100. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/dsa.py +0 -0
  101. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/ec.py +0 -0
  102. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/ec_params.py +0 -0
  103. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/kdf.py +0 -0
  104. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/misc.py +0 -0
  105. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/pq.py +0 -0
  106. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/rsa.py +0 -0
  107. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/umac.py +0 -0
  108. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/crypto/x509.py +0 -0
  109. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/dsa.py +0 -0
  110. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/ecdsa.py +0 -0
  111. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/eddsa.py +0 -0
  112. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/editor.py +0 -0
  113. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/encryption.py +0 -0
  114. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/gss.py +0 -0
  115. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/gss_unix.py +0 -0
  116. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/gss_win32.py +0 -0
  117. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/kex.py +0 -0
  118. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/kex_dh.py +0 -0
  119. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/kex_rsa.py +0 -0
  120. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/keysign.py +0 -0
  121. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/known_hosts.py +0 -0
  122. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/listener.py +0 -0
  123. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/logging.py +0 -0
  124. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/mac.py +0 -0
  125. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/packet.py +0 -0
  126. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/pattern.py +0 -0
  127. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/pbe.py +0 -0
  128. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/pkcs11.py +0 -0
  129. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/py.typed +0 -0
  130. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/rsa.py +0 -0
  131. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/saslprep.py +0 -0
  132. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/server.py +0 -0
  133. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/session.py +0 -0
  134. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/sk_ecdsa.py +0 -0
  135. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/sk_eddsa.py +0 -0
  136. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/socks.py +0 -0
  137. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/stream.py +0 -0
  138. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/subprocess.py +0 -0
  139. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/tuntap.py +0 -0
  140. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh/x11.py +0 -0
  141. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh.egg-info/SOURCES.txt +0 -0
  142. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh.egg-info/dependency_links.txt +0 -0
  143. {asyncssh-2.21.0 → asyncssh-2.22.0}/asyncssh.egg-info/top_level.txt +0 -0
  144. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/_templates/sidebarbottom.html +0 -0
  145. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/_templates/sidebartop.html +0 -0
  146. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/api.rst +0 -0
  147. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/conf.py +0 -0
  148. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/contributing.rst +0 -0
  149. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/index.rst +0 -0
  150. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/requirements.txt +0 -0
  151. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/rftheme/layout.html +0 -0
  152. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/rftheme/static/rftheme.css_t +0 -0
  153. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/rftheme/theme.conf +0 -0
  154. {asyncssh-2.21.0 → asyncssh-2.22.0}/docs/rtd-req.txt +0 -0
  155. {asyncssh-2.21.0 → asyncssh-2.22.0}/mypy.ini +0 -0
  156. {asyncssh-2.21.0 → asyncssh-2.22.0}/pylintrc +0 -0
  157. {asyncssh-2.21.0 → asyncssh-2.22.0}/setup.cfg +0 -0
  158. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/__init__.py +0 -0
  159. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/gss_stub.py +0 -0
  160. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/gssapi_stub.py +0 -0
  161. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/keysign_stub.py +0 -0
  162. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/pkcs11_stub.py +0 -0
  163. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/server.py +0 -0
  164. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/sspi_stub.py +0 -0
  165. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_asn1.py +0 -0
  166. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_auth.py +0 -0
  167. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_auth_keys.py +0 -0
  168. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_compression.py +0 -0
  169. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_editor.py +0 -0
  170. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_encryption.py +0 -0
  171. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_known_hosts.py +0 -0
  172. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_logging.py +0 -0
  173. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_mac.py +0 -0
  174. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_packet.py +0 -0
  175. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_process.py +0 -0
  176. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_saslprep.py +0 -0
  177. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_subprocess.py +0 -0
  178. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_x11.py +0 -0
  179. {asyncssh-2.21.0 → asyncssh-2.22.0}/tests/test_x509.py +0 -0
@@ -1,27 +1,20 @@
1
1
  name: Run tests
2
2
  on: [push, pull_request]
3
3
 
4
+ permissions:
5
+ contents: read
6
+
4
7
  jobs:
5
8
  run-tests:
6
9
  name: Run tests
10
+ permissions:
11
+ contents: read
12
+ actions: write
7
13
  strategy:
8
14
  fail-fast: false
9
15
  matrix:
10
16
  os: [ubuntu-latest, macos-latest, windows-latest]
11
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
12
- include:
13
- - os: macos-latest
14
- python-version: "3.10"
15
- openssl-version: "3"
16
- - os: macos-latest
17
- python-version: "3.11"
18
- openssl-version: "3"
19
- - os: macos-latest
20
- python-version: "3.12"
21
- openssl-version: "3"
22
- - os: macos-latest
23
- python-version: "3.13"
24
- openssl-version: "3"
17
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
25
18
 
26
19
  runs-on: ${{ matrix.os }}
27
20
  env:
@@ -50,6 +43,19 @@ jobs:
50
43
  asyncssh/setup.py
51
44
  asyncssh/tox.ini
52
45
 
46
+ - name: Fix modules path for OpenSSL on Windows
47
+ if: ${{ runner.os == 'Windows' }}
48
+ shell: pwsh
49
+ run: |
50
+ $openssl_path = ((Get-Command openssl).Path | Split-Path -Parent)
51
+ echo "OPENSSL_MODULES=$openssl_path" >> $env:GITHUB_ENV
52
+
53
+ - name: Verify legacy provider can be enabled in OpenSSL on Windows
54
+ if: ${{ runner.os == 'Windows' }}
55
+ shell: pwsh
56
+ run: |
57
+ openssl list -provider default -provider legacy -providers
58
+
53
59
  - name: Set up ccache for liboqs (Linux)
54
60
  uses: hendrikmuhs/ccache-action@v1.2
55
61
  if: ${{ runner.os == 'Linux' }}
@@ -60,15 +66,11 @@ jobs:
60
66
  if: ${{ runner.os == 'Linux' }}
61
67
  run: |
62
68
  sudo apt update
63
- sudo apt install -y --no-install-recommends libnettle8 libsodium-dev libssl-dev libkrb5-dev ssh cmake ninja-build
69
+ sudo apt install -y --no-install-recommends libnettle8 libssl-dev libkrb5-dev ssh cmake ninja-build
64
70
 
65
71
  - name: Install macOS dependencies
66
72
  if: ${{ runner.os == 'macOS' }}
67
- run: brew install nettle liboqs libsodium openssl
68
-
69
- - name: Provide OpenSSL 3
70
- if: ${{ runner.os == 'macOS' && matrix.openssl-version == '3' }}
71
- run: echo "/usr/local/opt/openssl@3/bin" >> $GITHUB_PATH
73
+ run: brew install nettle liboqs
72
74
 
73
75
  - name: Install nettle (Windows)
74
76
  if: ${{ runner.os == 'Windows' }}
@@ -124,6 +126,8 @@ jobs:
124
126
  runs-on: ubuntu-latest
125
127
  needs: run-tests
126
128
  if: ${{ always() }}
129
+ permissions:
130
+ actions: write
127
131
  steps:
128
132
  - name: Merge coverage
129
133
  uses: actions/upload-artifact/merge@v4
@@ -137,6 +141,9 @@ jobs:
137
141
  runs-on: ubuntu-latest
138
142
  needs: merge-coverage
139
143
  if: ${{ always() }}
144
+ permissions:
145
+ contents: read
146
+ actions: read
140
147
  steps:
141
148
  - uses: actions/checkout@v4
142
149
  - uses: actions/setup-python@v5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asyncssh
3
- Version: 2.21.0
3
+ Version: 2.22.0
4
4
  Summary: AsyncSSH: Asynchronous SSHv2 client and server library
5
5
  Author-email: Ron Frederick <ronf@timeheart.net>
6
6
  License: EPL-2.0 OR GPL-2.0-or-later
@@ -14,17 +14,16 @@ Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved
15
15
  Classifier: Operating System :: MacOS :: MacOS X
16
16
  Classifier: Operating System :: POSIX
17
- Classifier: Programming Language :: Python :: 3.8
18
- Classifier: Programming Language :: Python :: 3.9
19
17
  Classifier: Programming Language :: Python :: 3.10
20
18
  Classifier: Programming Language :: Python :: 3.11
21
19
  Classifier: Programming Language :: Python :: 3.12
22
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
23
22
  Classifier: Topic :: Internet
24
23
  Classifier: Topic :: Security :: Cryptography
25
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
25
  Classifier: Topic :: System :: Networking
27
- Requires-Python: >=3.6
26
+ Requires-Python: >=3.10
28
27
  Description-Content-Type: text/x-rst
29
28
  License-File: LICENSE
30
29
  Requires-Dist: cryptography>=39.0
@@ -32,7 +31,7 @@ Requires-Dist: typing_extensions>=4.0.0
32
31
  Provides-Extra: bcrypt
33
32
  Requires-Dist: bcrypt>=3.1.3; extra == "bcrypt"
34
33
  Provides-Extra: fido2
35
- Requires-Dist: fido2>=0.9.2; extra == "fido2"
34
+ Requires-Dist: fido2>=2; extra == "fido2"
36
35
  Provides-Extra: gssapi
37
36
  Requires-Dist: gssapi>=1.2.0; extra == "gssapi"
38
37
  Provides-Extra: libnacl
@@ -58,7 +57,7 @@ AsyncSSH: Asynchronous SSH for Python
58
57
  =====================================
59
58
 
60
59
  AsyncSSH is a Python package which provides an asynchronous client and
61
- server implementation of the SSHv2 protocol on top of the Python 3.6+
60
+ server implementation of the SSHv2 protocol on top of the Python 3.10+
62
61
  asyncio framework.
63
62
 
64
63
  .. code:: python
@@ -148,7 +147,7 @@ License
148
147
 
149
148
  This package is released under the following terms:
150
149
 
151
- Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
150
+ Copyright (c) 2013-2025 by Ron Frederick <ronf@timeheart.net> and others.
152
151
 
153
152
  This program and the accompanying materials are made available under
154
153
  the terms of the Eclipse Public License v2.0 which accompanies this
@@ -173,8 +172,8 @@ Prerequisites
173
172
 
174
173
  To use AsyncSSH 2.0 or later, you need the following:
175
174
 
176
- * Python 3.6 or later
177
- * cryptography (PyCA) 3.1 or later
175
+ * Python 3.10 or later
176
+ * cryptography (PyCA) 39.0 or later
178
177
 
179
178
  Installation
180
179
  ------------
@@ -207,12 +206,6 @@ functionality:
207
206
  if you want support for the OpenSSH post-quantum key exchange
208
207
  algorithms based on ML-KEM and SNTRUP.
209
208
 
210
- * Install libsodium from https://github.com/jedisct1/libsodium
211
- and libnacl from https://pypi.python.org/pypi/libnacl if you have
212
- a version of OpenSSL older than 1.1.1b installed and you want
213
- support for Curve25519 key exchange, Ed25519 keys and certificates,
214
- or the Chacha20-Poly1305 cipher.
215
-
216
209
  * Install libnettle from http://www.lysator.liu.se/~nisse/nettle/
217
210
  if you want support for UMAC cryptographic hashes.
218
211
 
@@ -229,28 +222,26 @@ easy to install any or all of these dependencies:
229
222
  | bcrypt
230
223
  | fido2
231
224
  | gssapi
232
- | libnacl
233
225
  | pkcs11
234
226
  | pyOpenSSL
235
227
  | pywin32
236
228
 
237
- For example, to install bcrypt, fido2, gssapi, libnacl, pkcs11, and
238
- pyOpenSSL on UNIX, you can run:
229
+ For example, to install bcrypt, fido2, gssapi, pkcs11, and pyOpenSSL
230
+ on UNIX, you can run:
239
231
 
240
232
  ::
241
233
 
242
- pip install 'asyncssh[bcrypt,fido2,gssapi,libnacl,pkcs11,pyOpenSSL]'
234
+ pip install 'asyncssh[bcrypt,fido2,gssapi,pkcs11,pyOpenSSL]'
243
235
 
244
- To install bcrypt, fido2, libnacl, pkcs11, pyOpenSSL, and pywin32 on
245
- Windows, you can run:
236
+ To install bcrypt, fido2, pkcs11, pyOpenSSL, and pywin32 on Windows,
237
+ you can run:
246
238
 
247
239
  ::
248
240
 
249
- pip install 'asyncssh[bcrypt,fido2,libnacl,pkcs11,pyOpenSSL,pywin32]'
241
+ pip install 'asyncssh[bcrypt,fido2,pkcs11,pyOpenSSL,pywin32]'
250
242
 
251
- Note that you will still need to manually install the libsodium library
252
- listed above for libnacl to work correctly and/or libnettle for UMAC
253
- support. Unfortunately, since liboqs, libsodium, and libnettle are not
243
+ Note that you will still need to manually install the libnettle library
244
+ for UMAC support. Unfortunately, since liboqs and libnettle are not
254
245
  Python packages, they cannot be directly installed using pip.
255
246
 
256
247
  Installing the development branch
@@ -11,7 +11,7 @@ AsyncSSH: Asynchronous SSH for Python
11
11
  =====================================
12
12
 
13
13
  AsyncSSH is a Python package which provides an asynchronous client and
14
- server implementation of the SSHv2 protocol on top of the Python 3.6+
14
+ server implementation of the SSHv2 protocol on top of the Python 3.10+
15
15
  asyncio framework.
16
16
 
17
17
  .. code:: python
@@ -101,7 +101,7 @@ License
101
101
 
102
102
  This package is released under the following terms:
103
103
 
104
- Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
104
+ Copyright (c) 2013-2025 by Ron Frederick <ronf@timeheart.net> and others.
105
105
 
106
106
  This program and the accompanying materials are made available under
107
107
  the terms of the Eclipse Public License v2.0 which accompanies this
@@ -126,8 +126,8 @@ Prerequisites
126
126
 
127
127
  To use AsyncSSH 2.0 or later, you need the following:
128
128
 
129
- * Python 3.6 or later
130
- * cryptography (PyCA) 3.1 or later
129
+ * Python 3.10 or later
130
+ * cryptography (PyCA) 39.0 or later
131
131
 
132
132
  Installation
133
133
  ------------
@@ -160,12 +160,6 @@ functionality:
160
160
  if you want support for the OpenSSH post-quantum key exchange
161
161
  algorithms based on ML-KEM and SNTRUP.
162
162
 
163
- * Install libsodium from https://github.com/jedisct1/libsodium
164
- and libnacl from https://pypi.python.org/pypi/libnacl if you have
165
- a version of OpenSSL older than 1.1.1b installed and you want
166
- support for Curve25519 key exchange, Ed25519 keys and certificates,
167
- or the Chacha20-Poly1305 cipher.
168
-
169
163
  * Install libnettle from http://www.lysator.liu.se/~nisse/nettle/
170
164
  if you want support for UMAC cryptographic hashes.
171
165
 
@@ -182,28 +176,26 @@ easy to install any or all of these dependencies:
182
176
  | bcrypt
183
177
  | fido2
184
178
  | gssapi
185
- | libnacl
186
179
  | pkcs11
187
180
  | pyOpenSSL
188
181
  | pywin32
189
182
 
190
- For example, to install bcrypt, fido2, gssapi, libnacl, pkcs11, and
191
- pyOpenSSL on UNIX, you can run:
183
+ For example, to install bcrypt, fido2, gssapi, pkcs11, and pyOpenSSL
184
+ on UNIX, you can run:
192
185
 
193
186
  ::
194
187
 
195
- pip install 'asyncssh[bcrypt,fido2,gssapi,libnacl,pkcs11,pyOpenSSL]'
188
+ pip install 'asyncssh[bcrypt,fido2,gssapi,pkcs11,pyOpenSSL]'
196
189
 
197
- To install bcrypt, fido2, libnacl, pkcs11, pyOpenSSL, and pywin32 on
198
- Windows, you can run:
190
+ To install bcrypt, fido2, pkcs11, pyOpenSSL, and pywin32 on Windows,
191
+ you can run:
199
192
 
200
193
  ::
201
194
 
202
- pip install 'asyncssh[bcrypt,fido2,libnacl,pkcs11,pyOpenSSL,pywin32]'
195
+ pip install 'asyncssh[bcrypt,fido2,pkcs11,pyOpenSSL,pywin32]'
203
196
 
204
- Note that you will still need to manually install the libsodium library
205
- listed above for libnacl to work correctly and/or libnettle for UMAC
206
- support. Unfortunately, since liboqs, libsodium, and libnettle are not
197
+ Note that you will still need to manually install the libnettle library
198
+ for UMAC support. Unfortunately, since liboqs and libnettle are not
207
199
  Python packages, they cannot be directly installed using pip.
208
200
 
209
201
  Installing the development branch
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
1
+ # Copyright (c) 2013-2025 by Ron Frederick <ronf@timeheart.net> and others.
2
2
  #
3
3
  # This program and the accompanying materials are made available under
4
4
  # the terms of the Eclipse Public License v2.0 which accompanies this
@@ -1125,10 +1125,12 @@ class SSHClientChannel(SSHChannel, Generic[AnyStr]):
1125
1125
  _read_datatypes = {EXTENDED_DATA_STDERR}
1126
1126
 
1127
1127
  def __init__(self, conn: 'SSHClientConnection',
1128
- loop: asyncio.AbstractEventLoop, encoding: Optional[str],
1129
- errors: str, window: int, max_pktsize: int):
1128
+ loop: asyncio.AbstractEventLoop, utf8_decode_errors: str,
1129
+ encoding: Optional[str], errors: str, window: int,
1130
+ max_pktsize: int):
1130
1131
  super().__init__(conn, loop, encoding, errors, window, max_pktsize)
1131
1132
 
1133
+ self._utf8_decode_errors = utf8_decode_errors
1132
1134
  self._exit_status: Optional[int] = None
1133
1135
  self._exit_signal: Optional[_ExitSignal] = None
1134
1136
 
@@ -1299,7 +1301,7 @@ class SSHClientChannel(SSHChannel, Generic[AnyStr]):
1299
1301
 
1300
1302
  try:
1301
1303
  signal = signal_bytes.decode('ascii')
1302
- msg = msg_bytes.decode('utf-8')
1304
+ msg = msg_bytes.decode('utf-8', self._utf8_decode_errors)
1303
1305
  lang = lang_bytes.decode('ascii')
1304
1306
  except UnicodeDecodeError:
1305
1307
  raise ProtocolError('Invalid exit signal request') from None
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
1
+ # Copyright (c) 2013-2025 by Ron Frederick <ronf@timeheart.net> and others.
2
2
  #
3
3
  # This program and the accompanying materials are made available under
4
4
  # the terms of the Eclipse Public License v2.0 which accompanies this
@@ -146,7 +146,7 @@ class SSHConfig:
146
146
  else:
147
147
  path = self._default_path
148
148
 
149
- paths = list(path.glob(pattern))
149
+ paths = list(p for p in path.glob(pattern) if p.is_file())
150
150
 
151
151
  if not paths:
152
152
  logger.debug1(f'Config pattern "{pattern}" matched no files')
@@ -877,6 +877,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
877
877
  self._peer_addr = ''
878
878
  self._peer_port = 0
879
879
  self._tcp_keepalive = options.tcp_keepalive
880
+ self._utf8_decode_errors = options.utf8_decode_errors
880
881
  self._owner: Optional[Union[SSHClient, SSHServer]] = None
881
882
  self._extra: Dict[str, object] = {}
882
883
 
@@ -1042,6 +1043,11 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
1042
1043
 
1043
1044
  return self._logger
1044
1045
 
1046
+ def _decode_utf8(self, msg_bytes) -> str:
1047
+ """Decode UTF-8 bytes, honoring utf8_decode_errors setting"""
1048
+
1049
+ return msg_bytes.decode('utf-8', self._utf8_decode_errors)
1050
+
1045
1051
  def _cleanup(self, exc: Optional[Exception]) -> None:
1046
1052
  """Clean up this connection"""
1047
1053
 
@@ -1550,8 +1556,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
1550
1556
 
1551
1557
  self._inpbuf = self._inpbuf[idx+1:]
1552
1558
 
1553
- if (version.startswith(b'SSH-2.0-') or
1554
- (self.is_client() and version.startswith(b'SSH-1.99-'))):
1559
+ if version.startswith(b'SSH-2.0-') or version.startswith(b'SSH-1.99-'):
1555
1560
  if len(version) > _MAX_VERSION_LINE_LEN:
1556
1561
  self._force_close(ProtocolError('Version too long'))
1557
1562
 
@@ -2194,7 +2199,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
2194
2199
  packet.check_end()
2195
2200
 
2196
2201
  try:
2197
- reason = reason_bytes.decode('utf-8')
2202
+ reason = self._decode_utf8(reason_bytes)
2198
2203
  lang = lang_bytes.decode('ascii')
2199
2204
  except UnicodeDecodeError:
2200
2205
  raise ProtocolError('Invalid disconnect message') from None
@@ -2237,7 +2242,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
2237
2242
  packet.check_end()
2238
2243
 
2239
2244
  try:
2240
- msg = msg_bytes.decode('utf-8')
2245
+ msg = self._decode_utf8(msg_bytes)
2241
2246
  lang = lang_bytes.decode('ascii')
2242
2247
  except UnicodeDecodeError:
2243
2248
  raise ProtocolError('Invalid debug message') from None
@@ -2639,7 +2644,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
2639
2644
  packet.check_end()
2640
2645
 
2641
2646
  try:
2642
- msg = msg_bytes.decode('utf-8')
2647
+ msg = self._decode_utf8(msg_bytes)
2643
2648
  lang = lang_bytes.decode('ascii')
2644
2649
  except UnicodeDecodeError:
2645
2650
  raise ProtocolError('Invalid userauth banner') from None
@@ -2756,7 +2761,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
2756
2761
  packet.check_end()
2757
2762
 
2758
2763
  try:
2759
- reason = reason_bytes.decode('utf-8')
2764
+ reason = self._decode_utf8(reason_bytes)
2760
2765
  lang = lang_bytes.decode('ascii')
2761
2766
  except UnicodeDecodeError:
2762
2767
  raise ProtocolError('Invalid channel open failure') from None
@@ -4169,7 +4174,7 @@ class SSHClientConnection(SSHConnection):
4169
4174
  async def create_session(self, session_factory: SSHClientSessionFactory,
4170
4175
  command: DefTuple[Optional[str]] = (), *,
4171
4176
  subsystem: DefTuple[Optional[str]]= (),
4172
- env: DefTuple[Env] = (),
4177
+ env: DefTuple[Optional[Env]] = (),
4173
4178
  send_env: DefTuple[Optional[EnvSeq]] = (),
4174
4179
  request_pty: DefTuple[Union[bool, str]] = (),
4175
4180
  term_type: DefTuple[Optional[str]] = (),
@@ -4374,8 +4379,8 @@ class SSHClientConnection(SSHConnection):
4374
4379
  window: int
4375
4380
  max_pktsize: int
4376
4381
 
4377
- chan = SSHClientChannel(self, self._loop, encoding, errors,
4378
- window, max_pktsize)
4382
+ chan = SSHClientChannel(self, self._loop, self._utf8_decode_errors,
4383
+ encoding, errors, window, max_pktsize)
4379
4384
 
4380
4385
  session = await chan.create(session_factory, command, subsystem,
4381
4386
  new_env, request_pty, term_type, term_size,
@@ -5687,7 +5692,7 @@ class SSHClientConnection(SSHConnection):
5687
5692
  return cast(SSHForwarder, peer)
5688
5693
 
5689
5694
  @async_context_manager
5690
- async def start_sftp_client(self, env: DefTuple[Env] = (),
5695
+ async def start_sftp_client(self, env: DefTuple[Optional[Env]] = (),
5691
5696
  send_env: DefTuple[Optional[EnvSeq]] = (),
5692
5697
  path_encoding: Optional[str] = 'utf-8',
5693
5698
  path_errors = 'strict',
@@ -5746,9 +5751,9 @@ class SSHClientConnection(SSHConnection):
5746
5751
  env=env, send_env=send_env,
5747
5752
  encoding=None)
5748
5753
 
5749
- return await start_sftp_client(self, self._loop, reader, writer,
5750
- path_encoding, path_errors,
5751
- sftp_version)
5754
+ return await start_sftp_client(self, self._loop,
5755
+ self._utf8_decode_errors, reader, writer,
5756
+ path_encoding, path_errors, sftp_version)
5752
5757
 
5753
5758
 
5754
5759
  class SSHServerConnection(SSHConnection):
@@ -7279,6 +7284,7 @@ class SSHConnectionOptions(Options, Generic[_Options]):
7279
7284
  family: int
7280
7285
  local_addr: HostPort
7281
7286
  tcp_keepalive: bool
7287
+ utf8_decode_errors: str
7282
7288
  canonicalize_hostname: Union[bool, str]
7283
7289
  canonical_domains: Sequence[str]
7284
7290
  canonicalize_fallback_local: bool
@@ -7324,6 +7330,7 @@ class SSHConnectionOptions(Options, Generic[_Options]):
7324
7330
  passphrase: Optional[BytesOrStr],
7325
7331
  proxy_command: DefTuple[_ProxyCommand], family: DefTuple[int],
7326
7332
  local_addr: DefTuple[HostPort], tcp_keepalive: DefTuple[bool],
7333
+ utf8_decode_errors: str,
7327
7334
  canonicalize_hostname: DefTuple[Union[bool, str]],
7328
7335
  canonical_domains: DefTuple[Sequence[str]],
7329
7336
  canonicalize_fallback_local: DefTuple[bool],
@@ -7388,6 +7395,8 @@ class SSHConnectionOptions(Options, Generic[_Options]):
7388
7395
  self.tcp_keepalive = cast(bool, tcp_keepalive if tcp_keepalive != ()
7389
7396
  else config.get('TCPKeepAlive', True))
7390
7397
 
7398
+ self.utf8_decode_errors = utf8_decode_errors
7399
+
7391
7400
  self.canonicalize_hostname = \
7392
7401
  cast(Union[bool, str], canonicalize_hostname
7393
7402
  if canonicalize_hostname != ()
@@ -7813,6 +7822,13 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
7813
7822
  :param tcp_keepalive: (optional)
7814
7823
  Whether or not to enable keepalive probes at the TCP level to
7815
7824
  detect broken connections, defaulting to `True`.
7825
+ :param utf8_decode_errors: (optional)
7826
+ Error handling strategy to apply when UTF-8 decode errors
7827
+ occur in SSH protocol messages, defaulting to 'strict'
7828
+ which shuts down the connection with a ProtocolError.
7829
+ Choosing other strategies can allow the message parsing
7830
+ to proceed with invalid bytes in the message being removed
7831
+ or replaced.
7816
7832
  :param canonicalize_hostname: (optional)
7817
7833
  Whether or not to enable hostname canonicalization, defaulting
7818
7834
  to `False`, in which case hostnames are passed as-is to the
@@ -7985,6 +8001,7 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
7985
8001
  :type keepalive_interval: *see* :ref:`SpecifyingTimeIntervals`
7986
8002
  :type keepalive_count_max: `int`
7987
8003
  :type tcp_keepalive: `bool`
8004
+ :type utf8_decode_errors: `str`
7988
8005
  :type canonicalize_hostname: `bool` or `'always'`
7989
8006
  :type canonical_domains: `list` of `str`
7990
8007
  :type canonicalize_fallback_local: `bool`
@@ -8042,7 +8059,7 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
8042
8059
  pkcs11_pin: Optional[str]
8043
8060
  command: Optional[str]
8044
8061
  subsystem: Optional[str]
8045
- env: Env
8062
+ env: Optional[Env]
8046
8063
  send_env: Optional[EnvSeq]
8047
8064
  request_pty: _RequestPTY
8048
8065
  term_type: Optional[str]
@@ -8070,6 +8087,7 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
8070
8087
  family: DefTuple[int] = (),
8071
8088
  local_addr: DefTuple[HostPort] = (),
8072
8089
  tcp_keepalive: DefTuple[bool] = (),
8090
+ utf8_decode_errors: str = 'strict',
8073
8091
  canonicalize_hostname: DefTuple[Union[bool, str]] = (),
8074
8092
  canonical_domains: DefTuple[Sequence[str]] = (),
8075
8093
  canonicalize_fallback_local: DefTuple[bool] = (),
@@ -8115,7 +8133,8 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
8115
8133
  pkcs11_provider: DefTuple[Optional[str]] = (),
8116
8134
  pkcs11_pin: Optional[str] = None,
8117
8135
  command: DefTuple[Optional[str]] = (),
8118
- subsystem: Optional[str] = None, env: DefTuple[Env] = (),
8136
+ subsystem: Optional[str] = None,
8137
+ env: DefTuple[Optional[Env]] = (),
8119
8138
  send_env: DefTuple[Optional[EnvSeq]] = (),
8120
8139
  request_pty: DefTuple[_RequestPTY] = (),
8121
8140
  term_type: Optional[str] = None,
@@ -8180,10 +8199,11 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
8180
8199
 
8181
8200
  super().prepare(config, client_factory or SSHClient, client_version,
8182
8201
  host, port, tunnel, passphrase, proxy_command, family,
8183
- local_addr, tcp_keepalive, canonicalize_hostname,
8184
- canonical_domains, canonicalize_fallback_local,
8185
- canonicalize_max_dots, canonicalize_permitted_cnames,
8186
- kex_algs, encryption_algs, mac_algs, compression_algs,
8202
+ local_addr, tcp_keepalive, utf8_decode_errors,
8203
+ canonicalize_hostname, canonical_domains,
8204
+ canonicalize_fallback_local, canonicalize_max_dots,
8205
+ canonicalize_permitted_cnames, kex_algs,
8206
+ encryption_algs, mac_algs, compression_algs,
8187
8207
  signature_algs, host_based_auth, public_key_auth,
8188
8208
  kbdint_auth, password_auth, x509_trusted_certs,
8189
8209
  x509_trusted_cert_paths, x509_purposes, rekey_bytes,
@@ -8354,7 +8374,8 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
8354
8374
 
8355
8375
  self.subsystem = subsystem
8356
8376
 
8357
- self.env = cast(Env, env if env != () else config.get('SetEnv'))
8377
+ self.env = cast(Optional[Env], env if env != () else
8378
+ config.get('SetEnv'))
8358
8379
 
8359
8380
  self.send_env = cast(Optional[EnvSeq], send_env if send_env != () else
8360
8381
  config.get('SendEnv'))
@@ -8635,6 +8656,13 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
8635
8656
  :param tcp_keepalive: (optional)
8636
8657
  Whether or not to enable keepalive probes at the TCP level to
8637
8658
  detect broken connections, defaulting to `True`.
8659
+ :param utf8_decode_errors: (optional)
8660
+ Error handling strategy to apply when UTF-8 decode errors
8661
+ occur in SSH protocol messages, defaulting to 'strict'
8662
+ which shuts down the connection with a ProtocolError.
8663
+ Choosing other strategies can allow the message parsing
8664
+ to proceed with invalid bytes in the message being removed
8665
+ or replaced.
8638
8666
  :param canonicalize_hostname: (optional)
8639
8667
  Whether or not to enable hostname canonicalization, defaulting
8640
8668
  to `False`, in which case hostnames are passed as-is to the
@@ -8731,6 +8759,7 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
8731
8759
  :type keepalive_interval: *see* :ref:`SpecifyingTimeIntervals`
8732
8760
  :type keepalive_count_max: `int`
8733
8761
  :type tcp_keepalive: `bool`
8762
+ :type utf8_decode_errors: `str`
8734
8763
  :type canonicalize_hostname: `bool` or `'always'`
8735
8764
  :type canonical_domains: `list` of `str`
8736
8765
  :type canonicalize_fallback_local: `bool`
@@ -8789,6 +8818,7 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
8789
8818
  family: DefTuple[int] = (),
8790
8819
  local_addr: DefTuple[HostPort] = (),
8791
8820
  tcp_keepalive: DefTuple[bool] = (),
8821
+ utf8_decode_errors: str = 'strict',
8792
8822
  canonicalize_hostname: DefTuple[Union[bool, str]] = (),
8793
8823
  canonical_domains: DefTuple[Sequence[str]] = (),
8794
8824
  canonicalize_fallback_local: DefTuple[bool] = (),
@@ -8864,10 +8894,11 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
8864
8894
 
8865
8895
  super().prepare(config, server_factory or SSHServer, server_version,
8866
8896
  host, port, tunnel, passphrase, proxy_command, family,
8867
- local_addr, tcp_keepalive, canonicalize_hostname,
8868
- canonical_domains, canonicalize_fallback_local,
8869
- canonicalize_max_dots, canonicalize_permitted_cnames,
8870
- kex_algs, encryption_algs, mac_algs, compression_algs,
8897
+ local_addr, tcp_keepalive, utf8_decode_errors,
8898
+ canonicalize_hostname, canonical_domains,
8899
+ canonicalize_fallback_local, canonicalize_max_dots,
8900
+ canonicalize_permitted_cnames, kex_algs,
8901
+ encryption_algs, mac_algs, compression_algs,
8871
8902
  signature_algs, host_based_auth, public_key_auth,
8872
8903
  kbdint_auth, password_auth, x509_trusted_certs,
8873
8904
  x509_trusted_cert_paths, x509_purposes,
@@ -62,11 +62,11 @@ __all__ = [
62
62
  'BasicCipher', 'ChachaCipher', 'CryptoKey', 'Curve25519DH', 'Curve448DH',
63
63
  'DH', 'DSAPrivateKey', 'DSAPublicKey', 'ECDH', 'ECDSAPrivateKey',
64
64
  'ECDSAPublicKey', 'EdDSAPrivateKey', 'EdDSAPublicKey', 'GCMCipher', 'PQDH',
65
- 'PyCAKey', 'RSAPrivateKey', 'RSAPublicKey', 'chacha_available',
66
- 'curve25519_available', 'curve448_available', 'X509Certificate',
67
- 'X509Name', 'X509NamePattern', 'ed25519_available', 'ed448_available',
68
- 'generate_x509_certificate', 'get_cipher_params', 'import_x509_certificate',
69
- 'lookup_ec_curve_by_params', 'mlkem_available', 'pbkdf2_hmac',
70
- 'register_cipher', 'sntrup_available', 'umac32', 'umac64', 'umac96',
71
- 'umac128'
65
+ 'PyCAKey', 'RSAPrivateKey', 'RSAPublicKey', 'X509Certificate',
66
+ 'X509Name', 'X509NamePattern', 'chacha_available', 'curve25519_available',
67
+ 'curve448_available', 'ed25519_available', 'ed448_available',
68
+ 'generate_x509_certificate', 'get_cipher_params',
69
+ 'import_x509_certificate', 'lookup_ec_curve_by_params', 'mlkem_available',
70
+ 'pbkdf2_hmac', 'register_cipher', 'sntrup_available', 'umac32', 'umac64',
71
+ 'umac96', 'umac128'
72
72
  ]