asyncssh 2.17.0__tar.gz → 2.18.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 (178) hide show
  1. {asyncssh-2.17.0 → asyncssh-2.18.0}/.github/workflows/run_tests.yml +3 -1
  2. {asyncssh-2.17.0/asyncssh.egg-info → asyncssh-2.18.0}/PKG-INFO +15 -9
  3. {asyncssh-2.17.0 → asyncssh-2.18.0}/README.rst +3 -4
  4. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/__init__.py +11 -10
  5. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/agent.py +15 -23
  6. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/agent_win32.py +1 -1
  7. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/asn1.py +11 -11
  8. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/auth.py +36 -31
  9. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/auth_keys.py +3 -3
  10. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/channel.py +66 -42
  11. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/compression.py +3 -3
  12. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/config.py +11 -12
  13. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/connection.py +129 -98
  14. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/__init__.py +15 -4
  15. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/cipher.py +1 -1
  16. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/ec.py +10 -9
  17. asyncssh-2.18.0/asyncssh/crypto/pq.py +103 -0
  18. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/umac.py +3 -3
  19. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/x509.py +4 -4
  20. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/ecdsa.py +3 -3
  21. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/gss_win32.py +4 -4
  22. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/kex.py +2 -2
  23. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/kex_dh.py +63 -52
  24. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/kex_rsa.py +2 -2
  25. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/known_hosts.py +11 -11
  26. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/listener.py +3 -3
  27. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/logging.py +7 -7
  28. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/misc.py +86 -15
  29. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/packet.py +11 -10
  30. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/process.py +6 -5
  31. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/public_key.py +24 -25
  32. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/saslprep.py +3 -3
  33. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/scp.py +25 -19
  34. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/sftp.py +221 -98
  35. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/sk.py +6 -1
  36. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/version.py +1 -1
  37. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/x11.py +3 -3
  38. {asyncssh-2.17.0 → asyncssh-2.18.0/asyncssh.egg-info}/PKG-INFO +15 -9
  39. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh.egg-info/SOURCES.txt +1 -1
  40. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/api.rst +8 -1
  41. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/changes.rst +71 -1
  42. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/callback_client.py +1 -1
  43. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/callback_math_server.py +2 -2
  44. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/chat_server.py +4 -4
  45. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/editor.py +2 -2
  46. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/gather_results.py +3 -3
  47. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/listening_client.py +1 -1
  48. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/local_forwarding_client2.py +1 -1
  49. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/math_server.py +2 -2
  50. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/show_environment.py +1 -1
  51. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/show_terminal.py +6 -7
  52. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/simple_cert_server.py +2 -2
  53. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/simple_keyed_server.py +4 -4
  54. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/simple_server.py +14 -10
  55. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/stream_listening_client.py +1 -1
  56. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/pkcs11_stub.py +2 -2
  57. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/server.py +2 -2
  58. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_agent.py +2 -2
  59. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_auth.py +15 -6
  60. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_auth_keys.py +3 -3
  61. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_channel.py +170 -26
  62. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_config.py +6 -6
  63. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_connection.py +48 -2
  64. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_connection_auth.py +24 -3
  65. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_forward.py +10 -14
  66. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_kex.py +104 -89
  67. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_known_hosts.py +2 -2
  68. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_logging.py +2 -2
  69. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_pkcs11.py +4 -0
  70. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_process.py +15 -20
  71. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_public_key.py +62 -65
  72. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_saslprep.py +5 -5
  73. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_sftp.py +85 -62
  74. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_sk.py +10 -7
  75. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_stream.py +1 -1
  76. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_tuntap.py +0 -4
  77. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_x509.py +1 -1
  78. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/util.py +16 -2
  79. asyncssh-2.17.0/asyncssh/crypto/sntrup.py +0 -88
  80. {asyncssh-2.17.0 → asyncssh-2.18.0}/.coveragerc +0 -0
  81. {asyncssh-2.17.0 → asyncssh-2.18.0}/.gitignore +0 -0
  82. {asyncssh-2.17.0 → asyncssh-2.18.0}/.readthedocs.yaml +0 -0
  83. {asyncssh-2.17.0 → asyncssh-2.18.0}/CONTRIBUTING.rst +0 -0
  84. {asyncssh-2.17.0 → asyncssh-2.18.0}/COPYRIGHT +0 -0
  85. {asyncssh-2.17.0 → asyncssh-2.18.0}/LICENSE +0 -0
  86. {asyncssh-2.17.0 → asyncssh-2.18.0}/MANIFEST.in +0 -0
  87. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/agent_unix.py +0 -0
  88. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/client.py +0 -0
  89. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/constants.py +0 -0
  90. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/chacha.py +0 -0
  91. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/dh.py +0 -0
  92. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/dsa.py +0 -0
  93. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/ec_params.py +0 -0
  94. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/ed.py +2 -2
  95. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/kdf.py +0 -0
  96. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/misc.py +0 -0
  97. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/crypto/rsa.py +0 -0
  98. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/dsa.py +0 -0
  99. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/eddsa.py +0 -0
  100. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/editor.py +0 -0
  101. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/encryption.py +0 -0
  102. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/forward.py +0 -0
  103. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/gss.py +0 -0
  104. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/gss_unix.py +0 -0
  105. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/keysign.py +0 -0
  106. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/mac.py +0 -0
  107. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/pattern.py +0 -0
  108. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/pbe.py +0 -0
  109. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/pkcs11.py +0 -0
  110. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/py.typed +0 -0
  111. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/rsa.py +0 -0
  112. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/server.py +0 -0
  113. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/session.py +0 -0
  114. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/sk_ecdsa.py +0 -0
  115. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/sk_eddsa.py +0 -0
  116. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/socks.py +0 -0
  117. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/stream.py +0 -0
  118. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/subprocess.py +0 -0
  119. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh/tuntap.py +0 -0
  120. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh.egg-info/dependency_links.txt +0 -0
  121. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh.egg-info/requires.txt +0 -0
  122. {asyncssh-2.17.0 → asyncssh-2.18.0}/asyncssh.egg-info/top_level.txt +0 -0
  123. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/_templates/sidebarbottom.html +0 -0
  124. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/_templates/sidebartop.html +0 -0
  125. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/conf.py +0 -0
  126. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/contributing.rst +0 -0
  127. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/index.rst +0 -0
  128. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/requirements.txt +0 -0
  129. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/rftheme/layout.html +0 -0
  130. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/rftheme/static/rftheme.css_t +0 -0
  131. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/rftheme/theme.conf +0 -0
  132. {asyncssh-2.17.0 → asyncssh-2.18.0}/docs/rtd-req.txt +0 -0
  133. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/callback_client2.py +0 -0
  134. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/callback_client3.py +0 -0
  135. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/check_exit_status.py +0 -0
  136. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/chroot_sftp_server.py +0 -0
  137. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/direct_client.py +0 -0
  138. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/direct_server.py +0 -0
  139. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/local_forwarding_client.py +0 -0
  140. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/local_forwarding_server.py +0 -0
  141. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/math_client.py +0 -0
  142. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/redirect_input.py +0 -0
  143. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/redirect_local_pipe.py +0 -0
  144. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/redirect_remote_pipe.py +0 -0
  145. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/redirect_server.py +0 -0
  146. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/remote_forwarding_client.py +0 -0
  147. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/remote_forwarding_client2.py +0 -0
  148. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/remote_forwarding_server.py +0 -0
  149. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/reverse_client.py +0 -0
  150. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/reverse_server.py +0 -0
  151. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/scp_client.py +0 -0
  152. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/set_environment.py +0 -0
  153. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/set_terminal.py +0 -0
  154. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/sftp_client.py +0 -0
  155. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/simple_client.py +0 -0
  156. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/simple_scp_server.py +0 -0
  157. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/simple_sftp_server.py +0 -0
  158. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/stream_direct_client.py +0 -0
  159. {asyncssh-2.17.0 → asyncssh-2.18.0}/examples/stream_direct_server.py +0 -0
  160. {asyncssh-2.17.0 → asyncssh-2.18.0}/mypy.ini +0 -0
  161. {asyncssh-2.17.0 → asyncssh-2.18.0}/pylintrc +0 -0
  162. {asyncssh-2.17.0 → asyncssh-2.18.0}/setup.cfg +0 -0
  163. {asyncssh-2.17.0 → asyncssh-2.18.0}/setup.py +0 -0
  164. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/__init__.py +0 -0
  165. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/gss_stub.py +0 -0
  166. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/gssapi_stub.py +0 -0
  167. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/keysign_stub.py +0 -0
  168. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/sk_stub.py +0 -0
  169. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/sspi_stub.py +0 -0
  170. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_asn1.py +0 -0
  171. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_compression.py +0 -0
  172. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_editor.py +0 -0
  173. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_encryption.py +0 -0
  174. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_mac.py +0 -0
  175. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_packet.py +0 -0
  176. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_subprocess.py +0 -0
  177. {asyncssh-2.17.0 → asyncssh-2.18.0}/tests/test_x11.py +0 -0
  178. {asyncssh-2.17.0 → asyncssh-2.18.0}/tox.ini +0 -0
@@ -38,7 +38,7 @@ jobs:
38
38
 
39
39
  runs-on: ${{ matrix.os }}
40
40
  env:
41
- liboqs_version: '0.7.2'
41
+ liboqs_version: '0.10.1'
42
42
  nettle_version: nettle_3.8.1_release_20220727
43
43
 
44
44
  steps:
@@ -130,6 +130,7 @@ jobs:
130
130
  with:
131
131
  name: coverage-${{ matrix.os }}-${{ matrix.python-version }}
132
132
  path: asyncssh/.coverage.*
133
+ include-hidden-files: true
133
134
  retention-days: 1
134
135
 
135
136
  merge-coverage:
@@ -142,6 +143,7 @@ jobs:
142
143
  with:
143
144
  name: coverage
144
145
  pattern: coverage-*
146
+ include-hidden-files: true
145
147
 
146
148
  report-coverage:
147
149
  name: Report coverage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: asyncssh
3
- Version: 2.17.0
3
+ Version: 2.18.0
4
4
  Summary: AsyncSSH: Asynchronous SSHv2 client and server library
5
5
  Home-page: http://asyncssh.timeheart.net
6
6
  Author: Ron Frederick
@@ -27,14 +27,23 @@ Classifier: Topic :: Security :: Cryptography
27
27
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
28
28
  Classifier: Topic :: System :: Networking
29
29
  Requires-Python: >= 3.6
30
+ License-File: LICENSE
31
+ Requires-Dist: cryptography>=39.0
32
+ Requires-Dist: typing_extensions>=4.0.0
30
33
  Provides-Extra: bcrypt
34
+ Requires-Dist: bcrypt>=3.1.3; extra == "bcrypt"
31
35
  Provides-Extra: fido2
36
+ Requires-Dist: fido2>=0.9.2; extra == "fido2"
32
37
  Provides-Extra: gssapi
38
+ Requires-Dist: gssapi>=1.2.0; extra == "gssapi"
33
39
  Provides-Extra: libnacl
40
+ Requires-Dist: libnacl>=1.4.2; extra == "libnacl"
34
41
  Provides-Extra: pkcs11
35
- Provides-Extra: pyOpenSSL
42
+ Requires-Dist: python-pkcs11>=0.7.0; extra == "pkcs11"
43
+ Provides-Extra: pyopenssl
44
+ Requires-Dist: pyOpenSSL>=23.0.0; extra == "pyopenssl"
36
45
  Provides-Extra: pywin32
37
- License-File: LICENSE
46
+ Requires-Dist: pywin32>=227; extra == "pywin32"
38
47
 
39
48
  .. image:: https://readthedocs.org/projects/asyncssh/badge/?version=latest
40
49
  :target: https://asyncssh.readthedocs.io/en/latest/?badge=latest
@@ -95,8 +104,7 @@ Features
95
104
  * Byte and string based I/O with settable encoding
96
105
  * A variety of `key exchange`__, `encryption`__, and `MAC`__ algorithms
97
106
 
98
- * Including OpenSSH post-quantum kex algorithm
99
- sntrup761x25519-sha512\@openssh.com
107
+ * Including post-quantum kex algorithms ML-KEM and SNTRUP
100
108
 
101
109
  * Support for `gzip compression`__
102
110
 
@@ -140,7 +148,7 @@ License
140
148
 
141
149
  This package is released under the following terms:
142
150
 
143
- Copyright (c) 2013-2022 by Ron Frederick <ronf@timeheart.net> and others.
151
+ Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
144
152
 
145
153
  This program and the accompanying materials are made available under
146
154
  the terms of the Eclipse Public License v2.0 which accompanies this
@@ -197,7 +205,7 @@ functionality:
197
205
 
198
206
  * Install liboqs from https://github.com/open-quantum-safe/liboqs
199
207
  if you want support for the OpenSSH post-quantum key exchange
200
- algorithm sntrup761x25519-sha512\@openssh.com.
208
+ algorithms based on ML-KEM and SNTRUP.
201
209
 
202
210
  * Install libsodium from https://github.com/jedisct1/libsodium
203
211
  and libnacl from https://pypi.python.org/pypi/libnacl if you have
@@ -267,5 +275,3 @@ Three mailing lists are available for AsyncSSH:
267
275
  __ http://groups.google.com/d/forum/asyncssh-announce
268
276
  __ http://groups.google.com/d/forum/asyncssh-dev
269
277
  __ http://groups.google.com/d/forum/asyncssh-users
270
-
271
-
@@ -57,8 +57,7 @@ Features
57
57
  * Byte and string based I/O with settable encoding
58
58
  * A variety of `key exchange`__, `encryption`__, and `MAC`__ algorithms
59
59
 
60
- * Including OpenSSH post-quantum kex algorithm
61
- sntrup761x25519-sha512\@openssh.com
60
+ * Including post-quantum kex algorithms ML-KEM and SNTRUP
62
61
 
63
62
  * Support for `gzip compression`__
64
63
 
@@ -102,7 +101,7 @@ License
102
101
 
103
102
  This package is released under the following terms:
104
103
 
105
- Copyright (c) 2013-2022 by Ron Frederick <ronf@timeheart.net> and others.
104
+ Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
106
105
 
107
106
  This program and the accompanying materials are made available under
108
107
  the terms of the Eclipse Public License v2.0 which accompanies this
@@ -159,7 +158,7 @@ functionality:
159
158
 
160
159
  * Install liboqs from https://github.com/open-quantum-safe/liboqs
161
160
  if you want support for the OpenSSH post-quantum key exchange
162
- algorithm sntrup761x25519-sha512\@openssh.com.
161
+ algorithms based on ML-KEM and SNTRUP.
163
162
 
164
163
  * Install libsodium from https://github.com/jedisct1/libsodium
165
164
  and libnacl from https://pypi.python.org/pypi/libnacl if you have
@@ -109,7 +109,7 @@ from .sftp import SFTPByteRangeLockRefused, SFTPDeletePending
109
109
  from .sftp import SFTPFileCorrupt, SFTPOwnerInvalid, SFTPGroupInvalid
110
110
  from .sftp import SFTPNoMatchingByteRangeLock
111
111
  from .sftp import SFTPConnectionLost, SFTPOpUnsupported
112
- from .sftp import SFTPAttrs, SFTPVFSAttrs, SFTPName
112
+ from .sftp import SFTPAttrs, SFTPVFSAttrs, SFTPName, SFTPLimits
113
113
  from .sftp import SEEK_SET, SEEK_CUR, SEEK_END
114
114
 
115
115
  from .stream import SSHSocketSessionFactory, SSHServerSessionFactory
@@ -122,6 +122,7 @@ from .subprocess import SSHSubprocessProtocol, SSHSubprocessTransport
122
122
  from . import sk_eddsa, sk_ecdsa, eddsa, ecdsa, rsa, dsa, kex_dh, kex_rsa
123
123
 
124
124
  __all__ = [
125
+ '__author__', '__author_email__', '__url__', '__version__',
125
126
  'BreakReceived', 'BytesOrStr', 'ChannelListenError',
126
127
  'ChannelOpenError', 'CompressionError', 'ConfigParseError',
127
128
  'ConnectionLost', 'DEVNULL', 'DataType', 'DisconnectError', 'Error',
@@ -136,15 +137,15 @@ __all__ = [
136
137
  'SFTPDirNotEmpty', 'SFTPEOFError', 'SFTPError', 'SFTPFailure',
137
138
  'SFTPFileAlreadyExists', 'SFTPFileCorrupt', 'SFTPFileIsADirectory',
138
139
  'SFTPGroupInvalid', 'SFTPInvalidFilename', 'SFTPInvalidHandle',
139
- 'SFTPInvalidParameter', 'SFTPLinkLoop', 'SFTPLockConflict', 'SFTPName',
140
- 'SFTPNoConnection', 'SFTPNoMatchingByteRangeLock', 'SFTPNoMedia',
141
- 'SFTPNoSpaceOnFilesystem', 'SFTPNoSuchFile', 'SFTPNoSuchPath',
142
- 'SFTPNotADirectory', 'SFTPOpUnsupported', 'SFTPOwnerInvalid',
143
- 'SFTPPermissionDenied', 'SFTPQuotaExceeded', 'SFTPServer',
144
- 'SFTPServerFactory', 'SFTPUnknownPrincipal', 'SFTPVFSAttrs',
145
- 'SFTPWriteProtect', 'SSHAcceptor', 'SSHAgentClient', 'SSHAgentKeyPair',
146
- 'SSHAuthorizedKeys', 'SSHCertificate', 'SSHClient', 'SSHClientChannel',
147
- 'SSHClientConnection', 'SSHClientConnectionOptions',
140
+ 'SFTPInvalidParameter', 'SFTPLimits', 'SFTPLinkLoop', 'SFTPLockConflict',
141
+ 'SFTPName', 'SFTPNoConnection', 'SFTPNoMatchingByteRangeLock',
142
+ 'SFTPNoMedia', 'SFTPNoSpaceOnFilesystem', 'SFTPNoSuchFile',
143
+ 'SFTPNoSuchPath', 'SFTPNotADirectory', 'SFTPOpUnsupported',
144
+ 'SFTPOwnerInvalid', 'SFTPPermissionDenied', 'SFTPQuotaExceeded',
145
+ 'SFTPServer', 'SFTPServerFactory', 'SFTPUnknownPrincipal', 'SFTPVFSAttrs',
146
+ 'SFTPWriteProtect', 'SSHAcceptHandler', 'SSHAcceptor', 'SSHAgentClient',
147
+ 'SSHAgentKeyPair', 'SSHAuthorizedKeys', 'SSHCertificate', 'SSHClient',
148
+ 'SSHClientChannel', 'SSHClientConnection', 'SSHClientConnectionOptions',
148
149
  'SSHClientProcess', 'SSHClientSession', 'SSHCompletedProcess',
149
150
  'SSHForwarder', 'SSHKey', 'SSHKeyPair', 'SSHKnownHosts',
150
151
  'SSHLineEditorChannel', 'SSHListener', 'SSHReader', 'SSHServer',
@@ -21,7 +21,6 @@
21
21
  """SSH agent client"""
22
22
 
23
23
  import asyncio
24
- import errno
25
24
  import os
26
25
  import sys
27
26
  from types import TracebackType
@@ -58,17 +57,10 @@ class AgentWriter(Protocol):
58
57
  """Wait for the connection to the SSH agent to close"""
59
58
 
60
59
 
61
- try:
62
- if sys.platform == 'win32': # pragma: no cover
63
- from .agent_win32 import open_agent
64
- else:
65
- from .agent_unix import open_agent
66
- except ImportError as _exc: # pragma: no cover
67
- async def open_agent(agent_path: str) -> \
68
- Tuple[AgentReader, AgentWriter]:
69
- """Dummy function if we're unable to import agent support"""
70
-
71
- raise OSError(errno.ENOENT, 'Agent support unavailable: %s' % str(_exc))
60
+ if sys.platform == 'win32': # pragma: no cover
61
+ from .agent_win32 import open_agent
62
+ else:
63
+ from .agent_unix import open_agent
72
64
 
73
65
 
74
66
  class _SupportsOpenAgentConnection(Protocol):
@@ -260,7 +252,7 @@ class SSHAgentClient:
260
252
 
261
253
  resplen = int.from_bytes((await reader.readexactly(4)), 'big')
262
254
 
263
- resp = SSHPacket((await reader.readexactly(resplen)))
255
+ resp = SSHPacket(await reader.readexactly(resplen))
264
256
  resptype = resp.get_byte()
265
257
 
266
258
  return resptype, resp
@@ -306,7 +298,7 @@ class SSHAgentClient:
306
298
  resp.check_end()
307
299
  return result
308
300
  else:
309
- raise ValueError('Unknown SSH agent response: %d' % resptype)
301
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
310
302
 
311
303
  async def sign(self, key_blob: bytes, data: bytes,
312
304
  flags: int = 0) -> bytes:
@@ -323,7 +315,7 @@ class SSHAgentClient:
323
315
  elif resptype == SSH_AGENT_FAILURE:
324
316
  raise ValueError('Unable to sign with requested key')
325
317
  else:
326
- raise ValueError('Unknown SSH agent response: %d' % resptype)
318
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
327
319
 
328
320
  async def add_keys(self, keylist: KeyPairListArg = (),
329
321
  passphrase: Optional[str] = None,
@@ -405,7 +397,7 @@ class SSHAgentClient:
405
397
  if not ignore_failures:
406
398
  raise ValueError('Unable to add key')
407
399
  else:
408
- raise ValueError('Unknown SSH agent response: %d' % resptype)
400
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
409
401
 
410
402
  async def add_smartcard_keys(self, provider: str,
411
403
  pin: Optional[str] = None,
@@ -446,7 +438,7 @@ class SSHAgentClient:
446
438
  elif resptype == SSH_AGENT_FAILURE:
447
439
  raise ValueError('Unable to add keys')
448
440
  else:
449
- raise ValueError('Unknown SSH agent response: %d' % resptype)
441
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
450
442
 
451
443
  async def remove_keys(self, keylist: Sequence[SSHKeyPair]) -> None:
452
444
  """Remove a key stored in the agent
@@ -469,7 +461,7 @@ class SSHAgentClient:
469
461
  elif resptype == SSH_AGENT_FAILURE:
470
462
  raise ValueError('Key not found')
471
463
  else:
472
- raise ValueError('Unknown SSH agent response: %d' % resptype)
464
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
473
465
 
474
466
  async def remove_smartcard_keys(self, provider: str,
475
467
  pin: Optional[str] = None) -> None:
@@ -495,7 +487,7 @@ class SSHAgentClient:
495
487
  elif resptype == SSH_AGENT_FAILURE:
496
488
  raise ValueError('Keys not found')
497
489
  else:
498
- raise ValueError('Unknown SSH agent response: %d' % resptype)
490
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
499
491
 
500
492
  async def remove_all(self) -> None:
501
493
  """Remove all keys stored in the agent
@@ -512,7 +504,7 @@ class SSHAgentClient:
512
504
  elif resptype == SSH_AGENT_FAILURE:
513
505
  raise ValueError('Unable to remove all keys')
514
506
  else:
515
- raise ValueError('Unknown SSH agent response: %d' % resptype)
507
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
516
508
 
517
509
  async def lock(self, passphrase: str) -> None:
518
510
  """Lock the agent using the specified passphrase
@@ -536,7 +528,7 @@ class SSHAgentClient:
536
528
  elif resptype == SSH_AGENT_FAILURE:
537
529
  raise ValueError('Unable to lock SSH agent')
538
530
  else:
539
- raise ValueError('Unknown SSH agent response: %d' % resptype)
531
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
540
532
 
541
533
  async def unlock(self, passphrase: str) -> None:
542
534
  """Unlock the agent using the specified passphrase
@@ -560,7 +552,7 @@ class SSHAgentClient:
560
552
  elif resptype == SSH_AGENT_FAILURE:
561
553
  raise ValueError('Unable to unlock SSH agent')
562
554
  else:
563
- raise ValueError('Unknown SSH agent response: %d' % resptype)
555
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
564
556
 
565
557
  async def query_extensions(self) -> Sequence[str]:
566
558
  """Return a list of extensions supported by the agent
@@ -589,7 +581,7 @@ class SSHAgentClient:
589
581
  elif resptype == SSH_AGENT_FAILURE:
590
582
  return []
591
583
  else:
592
- raise ValueError('Unknown SSH agent response: %d' % resptype)
584
+ raise ValueError(f'Unknown SSH agent response: {resptype}')
593
585
 
594
586
  def close(self) -> None:
595
587
  """Close the SSH agent connection
@@ -78,7 +78,7 @@ class _PageantTransport:
78
78
  """Transport to connect to Pageant agent on Windows"""
79
79
 
80
80
  def __init__(self) -> None:
81
- self._mapname = '%s%08x' % (_AGENT_NAME, win32api.GetCurrentThreadId())
81
+ self._mapname = f'{_AGENT_NAME}{win32api.GetCurrentThreadId():08x}'
82
82
 
83
83
  try:
84
84
  self._mapfile = mmapfile.mmapfile('', self._mapname,
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2013-2021 by Ron Frederick <ronf@timeheart.net> and others.
1
+ # Copyright (c) 2013-2024 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
@@ -169,8 +169,8 @@ class RawDERObject:
169
169
  self.content = content
170
170
 
171
171
  def __repr__(self) -> str:
172
- return ('RawDERObject(%s, %s, %r)' %
173
- (_asn1_class[self.asn1_class], self.tag, self.content))
172
+ return f'RawDERObject({_asn1_class[self.asn1_class]}, ' \
173
+ f'{self.tag}, {self.content!r})'
174
174
 
175
175
  def __eq__(self, other: object) -> bool:
176
176
  if not isinstance(other, RawDERObject): # pragma: no cover
@@ -213,10 +213,10 @@ class TaggedDERObject:
213
213
 
214
214
  def __repr__(self) -> str:
215
215
  if self.asn1_class == CONTEXT_SPECIFIC:
216
- return 'TaggedDERObject(%s, %r)' % (self.tag, self.value)
216
+ return f'TaggedDERObject({self.tag}, {self.value!r})'
217
217
  else:
218
- return ('TaggedDERObject(%s, %s, %r)' %
219
- (_asn1_class[self.asn1_class], self.tag, self.value))
218
+ return f'TaggedDERObject({_asn1_class[self.asn1_class]}, ' \
219
+ f'{self.tag}, {self.value!r})'
220
220
 
221
221
  def __eq__(self, other: object) -> bool:
222
222
  if not isinstance(other, TaggedDERObject): # pragma: no cover
@@ -469,7 +469,7 @@ class BitString(DERType):
469
469
  return result
470
470
 
471
471
  def __repr__(self) -> str:
472
- return "BitString('%s')" % self
472
+ return f"BitString('{self}')"
473
473
 
474
474
  def __eq__(self, other: object) -> bool:
475
475
  if not isinstance(other, BitString): # pragma: no cover
@@ -508,10 +508,10 @@ class IA5String(DERType):
508
508
  self.value = value
509
509
 
510
510
  def __str__(self) -> str:
511
- return '%s' % self.value.decode('ascii')
511
+ return self.value.decode('ascii')
512
512
 
513
513
  def __repr__(self) -> str:
514
- return 'IA5String(%r)' % self.value
514
+ return f'IA5String({self.value!r})'
515
515
 
516
516
  def __eq__(self, other: object) -> bool: # pragma: no cover
517
517
  if not isinstance(other, IA5String):
@@ -569,7 +569,7 @@ class ObjectIdentifier(DERType):
569
569
  return self.value
570
570
 
571
571
  def __repr__(self) -> str:
572
- return "ObjectIdentifier('%s')" % self.value
572
+ return f"ObjectIdentifier('{self.value}')"
573
573
 
574
574
  def __eq__(self, other: object) -> bool:
575
575
  if not isinstance(other, ObjectIdentifier): # pragma: no cover
@@ -685,7 +685,7 @@ def der_encode(value: object) -> bytes:
685
685
  identifier = cls.identifier
686
686
  content = cls.encode(value)
687
687
  else:
688
- raise ASN1EncodeError('Cannot DER encode type %s' % t.__name__)
688
+ raise ASN1EncodeError(f'Cannot DER encode type {t.__name__}')
689
689
 
690
690
  length = len(content)
691
691
  if length < 0x80:
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2013-2022 by Ron Frederick <ronf@timeheart.net> and others.
1
+ # Copyright (c) 2013-2024 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
@@ -27,6 +27,7 @@ from .constants import DEFAULT_LANG
27
27
  from .gss import GSSBase, GSSError
28
28
  from .logging import SSHLogger
29
29
  from .misc import ProtocolError, PasswordChangeRequired, get_symbol_names
30
+ from .misc import run_in_executor
30
31
  from .packet import Boolean, String, UInt32, SSHPacket, SSHPacketHandler
31
32
  from .public_key import SigningKey
32
33
  from .saslprep import saslprep, SASLPrepError
@@ -158,7 +159,7 @@ class _ClientGSSKexAuth(ClientAuth):
158
159
  await self.send_request(key=self._conn.get_gss_context(),
159
160
  trivial=False)
160
161
  else:
161
- self._conn.try_next_auth()
162
+ self._conn.try_next_auth(next_method=True)
162
163
 
163
164
 
164
165
  class _ClientGSSMICAuth(ClientAuth):
@@ -180,10 +181,10 @@ class _ClientGSSMICAuth(ClientAuth):
180
181
 
181
182
  self._gss = self._conn.get_gss_context()
182
183
  self._gss.reset()
183
- mechs = b''.join((String(mech) for mech in self._gss.mechs))
184
+ mechs = b''.join(String(mech) for mech in self._gss.mechs)
184
185
  await self.send_request(UInt32(len(self._gss.mechs)), mechs)
185
186
  else:
186
- self._conn.try_next_auth()
187
+ self._conn.try_next_auth(next_method=True)
187
188
 
188
189
  def _finish(self) -> None:
189
190
  """Finish client GSS MIC authentication"""
@@ -199,8 +200,8 @@ class _ClientGSSMICAuth(ClientAuth):
199
200
  else:
200
201
  self.send_packet(MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)
201
202
 
202
- def _process_response(self, _pkttype: int, _pktid: int,
203
- packet: SSHPacket) -> None:
203
+ async def _process_response(self, _pkttype: int, _pktid: int,
204
+ packet: SSHPacket) -> None:
204
205
  """Process a GSS response from the server"""
205
206
 
206
207
  mech = packet.get_string()
@@ -212,7 +213,7 @@ class _ClientGSSMICAuth(ClientAuth):
212
213
  raise ProtocolError('Mechanism mismatch')
213
214
 
214
215
  try:
215
- token = self._gss.step()
216
+ token = await run_in_executor(self._gss.step)
216
217
  assert token is not None
217
218
 
218
219
  self.send_packet(MSG_USERAUTH_GSSAPI_TOKEN, String(token))
@@ -223,10 +224,10 @@ class _ClientGSSMICAuth(ClientAuth):
223
224
  if exc.token:
224
225
  self.send_packet(MSG_USERAUTH_GSSAPI_ERRTOK, String(exc.token))
225
226
 
226
- self._conn.try_next_auth()
227
+ self._conn.try_next_auth(next_method=True)
227
228
 
228
- def _process_token(self, _pkttype: int, _pktid: int,
229
- packet: SSHPacket) -> None:
229
+ async def _process_token(self, _pkttype: int, _pktid: int,
230
+ packet: SSHPacket) -> None:
230
231
  """Process a GSS token from the server"""
231
232
 
232
233
  token: Optional[bytes] = packet.get_string()
@@ -235,7 +236,7 @@ class _ClientGSSMICAuth(ClientAuth):
235
236
  assert self._gss is not None
236
237
 
237
238
  try:
238
- token = self._gss.step(token)
239
+ token = await run_in_executor(self._gss.step, token)
239
240
 
240
241
  if token:
241
242
  self.send_packet(MSG_USERAUTH_GSSAPI_TOKEN, String(token))
@@ -246,7 +247,7 @@ class _ClientGSSMICAuth(ClientAuth):
246
247
  if exc.token:
247
248
  self.send_packet(MSG_USERAUTH_GSSAPI_ERRTOK, String(exc.token))
248
249
 
249
- self._conn.try_next_auth()
250
+ self._conn.try_next_auth(next_method=True)
250
251
 
251
252
  def _process_error(self, _pkttype: int, _pktid: int,
252
253
  packet: SSHPacket) -> None:
@@ -261,8 +262,8 @@ class _ClientGSSMICAuth(ClientAuth):
261
262
  self.logger.debug1('GSS error from server: %s', msg)
262
263
  self._got_error = True
263
264
 
264
- def _process_error_token(self, _pkttype: int, _pktid: int,
265
- packet: SSHPacket) -> None:
265
+ async def _process_error_token(self, _pkttype: int, _pktid: int,
266
+ packet: SSHPacket) -> None:
266
267
  """Process a GSS error token from the server"""
267
268
 
268
269
  token = packet.get_string()
@@ -271,7 +272,7 @@ class _ClientGSSMICAuth(ClientAuth):
271
272
  assert self._gss is not None
272
273
 
273
274
  try:
274
- self._gss.step(token)
275
+ await run_in_executor(self._gss.step, token)
275
276
  except GSSError as exc:
276
277
  if not self._got_error: # pragma: no cover
277
278
  self.logger.debug1('GSS error from server: %s', str(exc))
@@ -294,7 +295,7 @@ class _ClientHostBasedAuth(ClientAuth):
294
295
  await self._conn.host_based_auth_requested()
295
296
 
296
297
  if keypair is None:
297
- self._conn.try_next_auth()
298
+ self._conn.try_next_auth(next_method=True)
298
299
  return
299
300
 
300
301
  self.logger.debug1('Trying host based auth of user %s on host %s '
@@ -322,7 +323,7 @@ class _ClientPublicKeyAuth(ClientAuth):
322
323
  self._keypair = await self._conn.public_key_auth_requested()
323
324
 
324
325
  if self._keypair is None:
325
- self._conn.try_next_auth()
326
+ self._conn.try_next_auth(next_method=True)
326
327
  return
327
328
 
328
329
  self.logger.debug1('Trying public key auth with %s key',
@@ -340,10 +341,14 @@ class _ClientPublicKeyAuth(ClientAuth):
340
341
  self.logger.debug1('Signing request with %s key',
341
342
  self._keypair.algorithm)
342
343
 
343
- await self.send_request(Boolean(True),
344
- String(self._keypair.algorithm),
345
- String(self._keypair.public_data),
346
- key=self._keypair, trivial=False)
344
+ try:
345
+ await self.send_request(Boolean(True),
346
+ String(self._keypair.algorithm),
347
+ String(self._keypair.public_data),
348
+ key=self._keypair, trivial=False)
349
+ except ValueError as exc:
350
+ self.logger.debug1('Public key auth failed: %s', str(exc))
351
+ self._conn.try_next_auth()
347
352
 
348
353
  def _process_public_key_ok(self, _pkttype: int, _pktid: int,
349
354
  packet: SSHPacket) -> None:
@@ -377,7 +382,7 @@ class _ClientKbdIntAuth(ClientAuth):
377
382
  submethods = await self._conn.kbdint_auth_requested()
378
383
 
379
384
  if submethods is None:
380
- self._conn.try_next_auth()
385
+ self._conn.try_next_auth(next_method=True)
381
386
  return
382
387
 
383
388
  self.logger.debug1('Trying keyboard-interactive auth')
@@ -393,7 +398,7 @@ class _ClientKbdIntAuth(ClientAuth):
393
398
  lang, prompts)
394
399
 
395
400
  if responses is None:
396
- self._conn.try_next_auth()
401
+ self._conn.try_next_auth(next_method=True)
397
402
  return
398
403
 
399
404
  self.send_packet(MSG_USERAUTH_INFO_RESPONSE, UInt32(len(responses)),
@@ -454,7 +459,7 @@ class _ClientPasswordAuth(ClientAuth):
454
459
  password = await self._conn.password_auth_requested()
455
460
 
456
461
  if password is None:
457
- self._conn.try_next_auth()
462
+ self._conn.try_next_auth(next_method=True)
458
463
  return
459
464
 
460
465
  self.logger.debug1('Trying password auth')
@@ -469,7 +474,7 @@ class _ClientPasswordAuth(ClientAuth):
469
474
 
470
475
  if result == NotImplemented:
471
476
  # Password change not supported - move on to the next auth method
472
- self._conn.try_next_auth()
477
+ self._conn.try_next_auth(next_method=True)
473
478
  return
474
479
 
475
480
  self.logger.debug1('Trying to chsnge password')
@@ -649,15 +654,15 @@ class _ServerGSSMICAuth(ServerAuth):
649
654
  else:
650
655
  self.send_failure()
651
656
 
652
- def _process_token(self, _pkttype: int, _pktid: int,
653
- packet: SSHPacket) -> None:
657
+ async def _process_token(self, _pkttype: int, _pktid: int,
658
+ packet: SSHPacket) -> None:
654
659
  """Process a GSS token from the client"""
655
660
 
656
661
  token: Optional[bytes] = packet.get_string()
657
662
  packet.check_end()
658
663
 
659
664
  try:
660
- token = self._gss.step(token)
665
+ token = await run_in_executor(self._gss.step, token)
661
666
 
662
667
  if token:
663
668
  self.send_packet(MSG_USERAUTH_GSSAPI_TOKEN, String(token))
@@ -682,15 +687,15 @@ class _ServerGSSMICAuth(ServerAuth):
682
687
  else:
683
688
  self.send_failure()
684
689
 
685
- def _process_error_token(self, _pkttype: int, _pktid: int,
686
- packet: SSHPacket) -> None:
690
+ async def _process_error_token(self, _pkttype: int, _pktid: int,
691
+ packet: SSHPacket) -> None:
687
692
  """Process a GSS error token from the client"""
688
693
 
689
694
  token = packet.get_string()
690
695
  packet.check_end()
691
696
 
692
697
  try:
693
- self._gss.step(token)
698
+ await run_in_executor(self._gss.step, token)
694
699
  except GSSError as exc:
695
700
  self.logger.debug1('GSS error from client: %s', str(exc))
696
701
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2015-2021 by Ron Frederick <ronf@timeheart.net> and others.
1
+ # Copyright (c) 2015-2024 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
@@ -120,8 +120,8 @@ class _SSHAuthorizedKeyEntry:
120
120
  host = host[1:-1]
121
121
 
122
122
  port = None if port_str == '*' else int(port_str)
123
- except:
124
- raise ValueError('Illegal permitopen value: %s' % value) from None
123
+ except ValueError:
124
+ raise ValueError(f'Illegal permitopen value: {value}') from None
125
125
 
126
126
  permitted_opens = cast(Set[Tuple[str, Optional[int]]],
127
127
  self.options.setdefault(option, set()))