asyncssh 2.21.1__tar.gz → 2.23.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.1 → asyncssh-2.23.0}/.github/workflows/run_tests.yml +17 -21
  2. {asyncssh-2.21.1/asyncssh.egg-info → asyncssh-2.23.0}/PKG-INFO +22 -27
  3. {asyncssh-2.21.1 → asyncssh-2.23.0}/README.rst +16 -20
  4. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/agent.py +1 -3
  5. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/channel.py +6 -4
  6. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/client.py +1 -1
  7. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/config.py +41 -5
  8. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/connection.py +101 -45
  9. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/__init__.py +7 -7
  10. asyncssh-2.23.0/asyncssh/crypto/chacha.py +103 -0
  11. asyncssh-2.23.0/asyncssh/crypto/ed.py +186 -0
  12. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/pq.py +5 -3
  13. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/encryption.py +26 -2
  14. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/forward.py +8 -3
  15. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/kex.py +1 -1
  16. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/misc.py +1 -1
  17. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/process.py +1 -1
  18. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/scp.py +2 -1
  19. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/server.py +4 -0
  20. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/sftp.py +33 -23
  21. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/sk.py +23 -12
  22. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/version.py +2 -2
  23. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/x11.py +11 -9
  24. {asyncssh-2.21.1 → asyncssh-2.23.0/asyncssh.egg-info}/PKG-INFO +22 -27
  25. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh.egg-info/requires.txt +3 -3
  26. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/api.rst +1 -15
  27. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/changes.rst +75 -0
  28. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/callback_client.py +2 -2
  29. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/callback_client2.py +2 -2
  30. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/callback_client3.py +2 -2
  31. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/callback_math_server.py +2 -2
  32. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/chat_server.py +2 -2
  33. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/check_exit_status.py +2 -2
  34. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/chroot_sftp_server.py +2 -2
  35. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/direct_client.py +2 -2
  36. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/direct_server.py +2 -2
  37. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/editor.py +2 -2
  38. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/gather_results.py +8 -6
  39. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/listening_client.py +2 -2
  40. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/local_forwarding_client.py +2 -2
  41. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/local_forwarding_client2.py +2 -2
  42. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/local_forwarding_server.py +2 -2
  43. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/math_client.py +2 -2
  44. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/math_server.py +2 -2
  45. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/redirect_input.py +2 -2
  46. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/redirect_local_pipe.py +2 -2
  47. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/redirect_remote_pipe.py +2 -2
  48. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/redirect_server.py +2 -2
  49. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/remote_forwarding_client.py +2 -2
  50. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/remote_forwarding_client2.py +2 -2
  51. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/remote_forwarding_server.py +2 -2
  52. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/reverse_client.py +2 -2
  53. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/reverse_server.py +2 -2
  54. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/scp_client.py +2 -2
  55. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/set_environment.py +2 -2
  56. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/set_terminal.py +2 -2
  57. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/sftp_client.py +2 -2
  58. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/show_environment.py +2 -2
  59. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/show_terminal.py +2 -2
  60. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/simple_cert_server.py +2 -2
  61. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/simple_client.py +2 -2
  62. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/simple_keyed_server.py +2 -2
  63. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/simple_scp_server.py +2 -2
  64. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/simple_server.py +2 -2
  65. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/simple_sftp_server.py +2 -2
  66. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/stream_direct_client.py +2 -2
  67. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/stream_direct_server.py +2 -2
  68. {asyncssh-2.21.1 → asyncssh-2.23.0}/examples/stream_listening_client.py +2 -2
  69. {asyncssh-2.21.1 → asyncssh-2.23.0}/pyproject.toml +4 -5
  70. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/sk_stub.py +22 -6
  71. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_channel.py +27 -1
  72. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_config.py +33 -1
  73. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_connection.py +53 -10
  74. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_forward.py +25 -1
  75. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_kex.py +6 -5
  76. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_pkcs11.py +1 -1
  77. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_process.py +17 -22
  78. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_sftp.py +22 -0
  79. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_sk.py +1 -1
  80. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_stream.py +1 -1
  81. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_tuntap.py +1 -1
  82. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/util.py +1 -1
  83. {asyncssh-2.21.1 → asyncssh-2.23.0}/tox.ini +8 -6
  84. asyncssh-2.21.1/asyncssh/crypto/chacha.py +0 -162
  85. asyncssh-2.21.1/asyncssh/crypto/ed.py +0 -325
  86. {asyncssh-2.21.1 → asyncssh-2.23.0}/.coveragerc +0 -0
  87. {asyncssh-2.21.1 → asyncssh-2.23.0}/.gitignore +0 -0
  88. {asyncssh-2.21.1 → asyncssh-2.23.0}/.readthedocs.yaml +0 -0
  89. {asyncssh-2.21.1 → asyncssh-2.23.0}/CONTRIBUTING.rst +0 -0
  90. {asyncssh-2.21.1 → asyncssh-2.23.0}/COPYRIGHT +0 -0
  91. {asyncssh-2.21.1 → asyncssh-2.23.0}/LICENSE +0 -0
  92. {asyncssh-2.21.1 → asyncssh-2.23.0}/MANIFEST.in +0 -0
  93. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/__init__.py +0 -0
  94. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/agent_unix.py +0 -0
  95. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/agent_win32.py +0 -0
  96. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/asn1.py +0 -0
  97. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/auth.py +0 -0
  98. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/auth_keys.py +0 -0
  99. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/compression.py +0 -0
  100. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/constants.py +0 -0
  101. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/cipher.py +0 -0
  102. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/dh.py +0 -0
  103. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/dsa.py +0 -0
  104. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/ec.py +0 -0
  105. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/ec_params.py +0 -0
  106. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/kdf.py +0 -0
  107. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/misc.py +0 -0
  108. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/rsa.py +0 -0
  109. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/umac.py +0 -0
  110. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/crypto/x509.py +0 -0
  111. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/dsa.py +0 -0
  112. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/ecdsa.py +0 -0
  113. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/eddsa.py +0 -0
  114. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/editor.py +0 -0
  115. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/gss.py +0 -0
  116. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/gss_unix.py +0 -0
  117. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/gss_win32.py +0 -0
  118. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/kex_dh.py +0 -0
  119. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/kex_rsa.py +0 -0
  120. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/keysign.py +0 -0
  121. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/known_hosts.py +0 -0
  122. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/listener.py +0 -0
  123. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/logging.py +0 -0
  124. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/mac.py +0 -0
  125. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/packet.py +0 -0
  126. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/pattern.py +0 -0
  127. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/pbe.py +0 -0
  128. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/pkcs11.py +0 -0
  129. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/public_key.py +0 -0
  130. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/py.typed +0 -0
  131. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/rsa.py +0 -0
  132. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/saslprep.py +0 -0
  133. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/session.py +0 -0
  134. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/sk_ecdsa.py +0 -0
  135. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/sk_eddsa.py +0 -0
  136. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/socks.py +0 -0
  137. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/stream.py +0 -0
  138. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/subprocess.py +0 -0
  139. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh/tuntap.py +0 -0
  140. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh.egg-info/SOURCES.txt +0 -0
  141. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh.egg-info/dependency_links.txt +0 -0
  142. {asyncssh-2.21.1 → asyncssh-2.23.0}/asyncssh.egg-info/top_level.txt +0 -0
  143. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/_templates/sidebarbottom.html +0 -0
  144. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/_templates/sidebartop.html +0 -0
  145. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/conf.py +0 -0
  146. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/contributing.rst +0 -0
  147. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/index.rst +0 -0
  148. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/requirements.txt +0 -0
  149. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/rftheme/layout.html +0 -0
  150. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/rftheme/static/rftheme.css_t +0 -0
  151. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/rftheme/theme.conf +0 -0
  152. {asyncssh-2.21.1 → asyncssh-2.23.0}/docs/rtd-req.txt +0 -0
  153. {asyncssh-2.21.1 → asyncssh-2.23.0}/mypy.ini +0 -0
  154. {asyncssh-2.21.1 → asyncssh-2.23.0}/pylintrc +0 -0
  155. {asyncssh-2.21.1 → asyncssh-2.23.0}/setup.cfg +0 -0
  156. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/__init__.py +0 -0
  157. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/gss_stub.py +0 -0
  158. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/gssapi_stub.py +0 -0
  159. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/keysign_stub.py +0 -0
  160. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/pkcs11_stub.py +0 -0
  161. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/server.py +0 -0
  162. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/sspi_stub.py +0 -0
  163. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_agent.py +0 -0
  164. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_asn1.py +0 -0
  165. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_auth.py +0 -0
  166. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_auth_keys.py +0 -0
  167. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_compression.py +0 -0
  168. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_connection_auth.py +0 -0
  169. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_editor.py +0 -0
  170. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_encryption.py +0 -0
  171. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_known_hosts.py +0 -0
  172. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_logging.py +0 -0
  173. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_mac.py +0 -0
  174. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_packet.py +0 -0
  175. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_public_key.py +0 -0
  176. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_saslprep.py +0 -0
  177. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_subprocess.py +0 -0
  178. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_x11.py +0 -0
  179. {asyncssh-2.21.1 → asyncssh-2.23.0}/tests/test_x509.py +0 -0
@@ -13,21 +13,8 @@ jobs:
13
13
  strategy:
14
14
  fail-fast: false
15
15
  matrix:
16
- os: [ubuntu-latest, macos-latest, windows-2022]
17
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
18
- include:
19
- - os: macos-latest
20
- python-version: "3.10"
21
- openssl-version: "3"
22
- - os: macos-latest
23
- python-version: "3.11"
24
- openssl-version: "3"
25
- - os: macos-latest
26
- python-version: "3.12"
27
- openssl-version: "3"
28
- - os: macos-latest
29
- python-version: "3.13"
30
- openssl-version: "3"
16
+ os: [ubuntu-latest, macos-latest, windows-latest]
17
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
31
18
 
32
19
  runs-on: ${{ matrix.os }}
33
20
  env:
@@ -56,6 +43,19 @@ jobs:
56
43
  asyncssh/setup.py
57
44
  asyncssh/tox.ini
58
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
+
59
59
  - name: Set up ccache for liboqs (Linux)
60
60
  uses: hendrikmuhs/ccache-action@v1.2
61
61
  if: ${{ runner.os == 'Linux' }}
@@ -66,15 +66,11 @@ jobs:
66
66
  if: ${{ runner.os == 'Linux' }}
67
67
  run: |
68
68
  sudo apt update
69
- 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
70
70
 
71
71
  - name: Install macOS dependencies
72
72
  if: ${{ runner.os == 'macOS' }}
73
- run: brew install nettle liboqs libsodium openssl
74
-
75
- - name: Provide OpenSSL 3
76
- if: ${{ runner.os == 'macOS' && matrix.openssl-version == '3' }}
77
- run: echo "/usr/local/opt/openssl@3/bin" >> $GITHUB_PATH
73
+ run: brew install nettle liboqs
78
74
 
79
75
  - name: Install nettle (Windows)
80
76
  if: ${{ runner.os == 'Windows' }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asyncssh
3
- Version: 2.21.1
3
+ Version: 2.23.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,11 +31,11 @@ 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<2,>=0.9.2; extra == "fido2"
34
+ Requires-Dist: fido2>=2; extra == "fido2"
35
+ Provides-Extra: ifaddr
36
+ Requires-Dist: ifaddr>=0.2.0; extra == "ifaddr"
36
37
  Provides-Extra: gssapi
37
38
  Requires-Dist: gssapi>=1.2.0; extra == "gssapi"
38
- Provides-Extra: libnacl
39
- Requires-Dist: libnacl>=1.4.2; extra == "libnacl"
40
39
  Provides-Extra: pkcs11
41
40
  Requires-Dist: python-pkcs11>=0.7.0; extra == "pkcs11"
42
41
  Provides-Extra: pyopenssl
@@ -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
  ------------
@@ -197,6 +196,9 @@ functionality:
197
196
  * Install fido2 from https://pypi.org/project/fido2 if you want support
198
197
  for key exchange and authentication with U2F/FIDO2 security keys.
199
198
 
199
+ * Install ifaddr from https://pypi.org/project/ifaddr/ if you want
200
+ support for matching on local network IP addresses.
201
+
200
202
  * Install python-pkcs11 from https://pypi.org/project/python-pkcs11 if
201
203
  you want support for accessing PIV keys on PKCS#11 security tokens.
202
204
 
@@ -207,12 +209,6 @@ functionality:
207
209
  if you want support for the OpenSSH post-quantum key exchange
208
210
  algorithms based on ML-KEM and SNTRUP.
209
211
 
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
212
  * Install libnettle from http://www.lysator.liu.se/~nisse/nettle/
217
213
  if you want support for UMAC cryptographic hashes.
218
214
 
@@ -228,29 +224,28 @@ easy to install any or all of these dependencies:
228
224
 
229
225
  | bcrypt
230
226
  | fido2
227
+ | ifaddr
231
228
  | gssapi
232
- | libnacl
233
229
  | pkcs11
234
230
  | pyOpenSSL
235
231
  | pywin32
236
232
 
237
- For example, to install bcrypt, fido2, gssapi, libnacl, pkcs11, and
238
- pyOpenSSL on UNIX, you can run:
233
+ For example, to install bcrypt, fido2, gssapi, ifaddr, pkcs11, and pyOpenSSL
234
+ on UNIX, you can run:
239
235
 
240
236
  ::
241
237
 
242
- pip install 'asyncssh[bcrypt,fido2,gssapi,libnacl,pkcs11,pyOpenSSL]'
238
+ pip install 'asyncssh[bcrypt,fido2,gssapi,ifaddr,pkcs11,pyOpenSSL]'
243
239
 
244
- To install bcrypt, fido2, libnacl, pkcs11, pyOpenSSL, and pywin32 on
245
- Windows, you can run:
240
+ To install bcrypt, fido2, ifaddr, pkcs11, pyOpenSSL, and pywin32 on Windows,
241
+ you can run:
246
242
 
247
243
  ::
248
244
 
249
- pip install 'asyncssh[bcrypt,fido2,libnacl,pkcs11,pyOpenSSL,pywin32]'
245
+ pip install 'asyncssh[bcrypt,fido2,ifaddr,pkcs11,pyOpenSSL,pywin32]'
250
246
 
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
247
+ Note that you will still need to manually install the libnettle library
248
+ for UMAC support. Unfortunately, since liboqs and libnettle are not
254
249
  Python packages, they cannot be directly installed using pip.
255
250
 
256
251
  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
  ------------
@@ -150,6 +150,9 @@ functionality:
150
150
  * Install fido2 from https://pypi.org/project/fido2 if you want support
151
151
  for key exchange and authentication with U2F/FIDO2 security keys.
152
152
 
153
+ * Install ifaddr from https://pypi.org/project/ifaddr/ if you want
154
+ support for matching on local network IP addresses.
155
+
153
156
  * Install python-pkcs11 from https://pypi.org/project/python-pkcs11 if
154
157
  you want support for accessing PIV keys on PKCS#11 security tokens.
155
158
 
@@ -160,12 +163,6 @@ functionality:
160
163
  if you want support for the OpenSSH post-quantum key exchange
161
164
  algorithms based on ML-KEM and SNTRUP.
162
165
 
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
166
  * Install libnettle from http://www.lysator.liu.se/~nisse/nettle/
170
167
  if you want support for UMAC cryptographic hashes.
171
168
 
@@ -181,29 +178,28 @@ easy to install any or all of these dependencies:
181
178
 
182
179
  | bcrypt
183
180
  | fido2
181
+ | ifaddr
184
182
  | gssapi
185
- | libnacl
186
183
  | pkcs11
187
184
  | pyOpenSSL
188
185
  | pywin32
189
186
 
190
- For example, to install bcrypt, fido2, gssapi, libnacl, pkcs11, and
191
- pyOpenSSL on UNIX, you can run:
187
+ For example, to install bcrypt, fido2, gssapi, ifaddr, pkcs11, and pyOpenSSL
188
+ on UNIX, you can run:
192
189
 
193
190
  ::
194
191
 
195
- pip install 'asyncssh[bcrypt,fido2,gssapi,libnacl,pkcs11,pyOpenSSL]'
192
+ pip install 'asyncssh[bcrypt,fido2,gssapi,ifaddr,pkcs11,pyOpenSSL]'
196
193
 
197
- To install bcrypt, fido2, libnacl, pkcs11, pyOpenSSL, and pywin32 on
198
- Windows, you can run:
194
+ To install bcrypt, fido2, ifaddr, pkcs11, pyOpenSSL, and pywin32 on Windows,
195
+ you can run:
199
196
 
200
197
  ::
201
198
 
202
- pip install 'asyncssh[bcrypt,fido2,libnacl,pkcs11,pyOpenSSL,pywin32]'
199
+ pip install 'asyncssh[bcrypt,fido2,ifaddr,pkcs11,pyOpenSSL,pywin32]'
203
200
 
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
201
+ Note that you will still need to manually install the libnettle library
202
+ for UMAC support. Unfortunately, since liboqs and libnettle are not
207
203
  Python packages, they cannot be directly installed using pip.
208
204
 
209
205
  Installing the development branch
@@ -121,9 +121,7 @@ class SSHAgentKeyPair(SSHKeyPair):
121
121
  else:
122
122
  sig_algorithm = algorithm
123
123
 
124
- # Neither Pageant nor the Win10 OpenSSH agent seems to support the
125
- # ssh-agent protocol flags used to request RSA SHA2 signatures yet
126
- if sig_algorithm == b'ssh-rsa' and sys.platform != 'win32':
124
+ if sig_algorithm == b'ssh-rsa':
127
125
  sig_algorithms: Sequence[bytes] = \
128
126
  (b'rsa-sha2-256', b'rsa-sha2-512', b'ssh-rsa')
129
127
  else:
@@ -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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2024 by Ron Frederick <ronf@timeheart.net> and others.
1
+ # Copyright (c) 2020-2026 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
@@ -29,14 +29,20 @@ import subprocess
29
29
  from hashlib import sha1
30
30
  from pathlib import Path, PurePath
31
31
  from subprocess import DEVNULL
32
- from typing import Callable, Dict, List, NoReturn, Optional, Sequence
33
- from typing import Set, Tuple, Union, cast
32
+ from typing import Callable, Dict, Iterator, List, NoReturn, Optional
33
+ from typing import Sequence, Set, Tuple, Union, cast
34
34
 
35
35
  from .constants import DEFAULT_PORT
36
36
  from .logging import logger
37
- from .misc import DefTuple, FilePath, ip_address
37
+ from .misc import DefTuple, FilePath, IllegalUserName, ip_address
38
38
  from .pattern import HostPatternList, WildcardPatternList
39
39
 
40
+ try:
41
+ import ifaddr
42
+ _ifaddr_available = True
43
+ except ImportError: # pragma: no cover
44
+ _ifaddr_available = False
45
+
40
46
 
41
47
  ConfigPaths = Union[None, FilePath, Sequence[FilePath]]
42
48
 
@@ -52,6 +58,18 @@ def _exec(cmd: str) -> bool:
52
58
  stdout=DEVNULL, stderr=DEVNULL).returncode == 0
53
59
 
54
60
 
61
+ def _get_local_ips() -> Iterator[str]:
62
+ """Return local IP addresses of the system"""
63
+
64
+ for adapter in ifaddr.get_adapters():
65
+ for ip in adapter.ips:
66
+ if isinstance(ip.ip, tuple):
67
+ addr, _, scope_id = ip.ip
68
+ yield f'{addr}%{scope_id}' if scope_id else addr
69
+ else:
70
+ yield ip.ip
71
+
72
+
55
73
  class ConfigParseError(ValueError):
56
74
  """Configuration parsing exception"""
57
75
 
@@ -146,7 +164,7 @@ class SSHConfig:
146
164
  else:
147
165
  path = self._default_path
148
166
 
149
- paths = list(path.glob(pattern))
167
+ paths = list(p for p in path.glob(pattern) if p.is_file())
150
168
 
151
169
  if not paths:
152
170
  logger.debug1(f'Config pattern "{pattern}" matched no files')
@@ -183,6 +201,10 @@ class SSHConfig:
183
201
  elif match == 'final':
184
202
  result = cast(bool, self._final)
185
203
  else:
204
+ if (match == 'localnetwork' and
205
+ not _ifaddr_available): # pragma: no cover
206
+ self._error('Local network match requires ifaddr module')
207
+
186
208
  match_val = self._match_val(match)
187
209
 
188
210
  if match != 'exec' and match_val is None:
@@ -201,6 +223,15 @@ class SSHConfig:
201
223
  ip = ip_address(cast(str, match_val)) \
202
224
  if match_val else None
203
225
  result = host_pat.matches(None, match_val, ip)
226
+ elif match == 'localnetwork':
227
+ host_pat = HostPatternList(arg)
228
+
229
+ for addr in cast(Iterator[str], match_val):
230
+ if host_pat.matches(None, addr, ip_address(addr)):
231
+ result = True
232
+ break
233
+ else:
234
+ result = False
204
235
  else:
205
236
  wild_pat = WildcardPatternList(arg)
206
237
  result = wild_pat.matches(match_val)
@@ -514,6 +545,8 @@ class SSHClientConfig(SSHConfig):
514
545
  return self._options.get('Hostname', self._orig_host)
515
546
  elif match == 'originalhost':
516
547
  return self._orig_host
548
+ elif match == 'localnetwork':
549
+ return _get_local_ips()
517
550
  elif match == 'localuser':
518
551
  return self._local_user
519
552
  elif match == 'user':
@@ -679,6 +712,9 @@ class SSHServerConfig(SSHConfig):
679
712
  def _set_tokens(self) -> None:
680
713
  """Set the tokens available for percent expansion"""
681
714
 
715
+ if self._user == '..' or '/' in self._user or '\\' in self._user:
716
+ raise IllegalUserName('Unsafe username substitution')
717
+
682
718
  self._tokens.update({'u': self._user})
683
719
 
684
720
  _handlers = {option.lower(): (option, handler) for option, handler in (