summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Forcier <jeff@bitprophet.org>2021-12-23 15:13:54 -0500
committerJeff Forcier <jeff@bitprophet.org>2021-12-23 15:58:54 -0500
commit2b66625659e66858cb5f557325c5fdd9c35fd073 (patch)
tree7760ef34e6a5f1727741f31ef57c524f30a3a308
parent363a28d94cada17f012c1604a3c99c71a2bda003 (diff)
downloadparamiko-rfc8832-sha2-key-algo.tar.gz
Add agent RSA-SHA2 support, also tweak changelog w/ more ticketsrfc8832-sha2-key-algo
-rw-r--r--paramiko/agent.py14
-rw-r--r--sites/www/changelog.rst12
-rw-r--r--tests/test_agent.py50
3 files changed, 71 insertions, 5 deletions
diff --git a/paramiko/agent.py b/paramiko/agent.py
index 3a02c06c..f28bf128 100644
--- a/paramiko/agent.py
+++ b/paramiko/agent.py
@@ -42,6 +42,18 @@ SSH2_AGENT_IDENTITIES_ANSWER = 12
cSSH2_AGENTC_SIGN_REQUEST = byte_chr(13)
SSH2_AGENT_SIGN_RESPONSE = 14
+SSH_AGENT_RSA_SHA2_256 = 2
+SSH_AGENT_RSA_SHA2_512 = 4
+# NOTE: RFC mildly confusing; while these flags are OR'd together, OpenSSH at
+# least really treats them like "AND"s, in the sense that if it finds the
+# SHA256 flag set it won't continue looking at the SHA512 one; it
+# short-circuits right away.
+# Thus, we never want to eg submit 6 to say "either's good".
+ALGORITHM_FLAG_MAP = {
+ "rsa-sha2-256": SSH_AGENT_RSA_SHA2_256,
+ "rsa-sha2-512": SSH_AGENT_RSA_SHA2_512,
+}
+
class AgentSSH(object):
def __init__(self):
@@ -416,7 +428,7 @@ class AgentKey(PKey):
msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
msg.add_string(self.blob)
msg.add_string(data)
- msg.add_int(0)
+ msg.add_int(ALGORITHM_FLAG_MAP.get(algorithm, 0))
ptype, result = self.agent._send_message(msg)
if ptype != SSH2_AGENT_SIGN_RESPONSE:
raise SSHException("key cannot be used for signing")
diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst
index 016a5ac9..a519d333 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -2,10 +2,11 @@
Changelog
=========
-- :feature:`1643` Add support for SHA-2 variants of RSA key verification
- algorithms (as described in :rfc:`8332`) as well as limited SSH extension
- negotiation (:rfc:`8308`). How SSH servers/clients decide when and how to use
- this functionality can be complicated; Paramiko's support is as follows:
+- :feature:`1643` (also :issue:`1925`, :issue:`1644`, :issue:`1326`) Add
+ support for SHA-2 variants of RSA key verification algorithms (as described
+ in :rfc:`8332`) as well as limited SSH extension negotiation (:rfc:`8308`).
+ How SSH servers/clients decide when and how to use this functionality can be
+ complicated; Paramiko's support is as follows:
- Client verification of server host key during key exchange will now prefer
``rsa-sha2-512``, ``rsa-sha2-256``, and legacy ``ssh-rsa`` algorithms, in
@@ -35,6 +36,9 @@ Changelog
supported by both ends is used, or if there is none, it falls back to the
previous behavior.
+ - SSH agent support grew the ability to specify algorithm flags when
+ requesting private key signatures; this is now used to forward SHA2
+ algorithms when appropriate.
- Server mode is now capable of pubkey auth involving SHA-2 signatures from
clients, provided one's server implementation actually provides for doing
so.
diff --git a/tests/test_agent.py b/tests/test_agent.py
new file mode 100644
index 00000000..c3973bbb
--- /dev/null
+++ b/tests/test_agent.py
@@ -0,0 +1,50 @@
+import unittest
+
+from paramiko.message import Message
+from paramiko.agent import (
+ SSH2_AGENT_SIGN_RESPONSE,
+ cSSH2_AGENTC_SIGN_REQUEST,
+ SSH_AGENT_RSA_SHA2_256,
+ SSH_AGENT_RSA_SHA2_512,
+ AgentKey,
+)
+from paramiko.py3compat import b
+
+
+class ChaosAgent:
+ def _send_message(self, msg):
+ self._sent_message = msg
+ sig = Message()
+ sig.add_string(b("lol"))
+ sig.rewind()
+ return SSH2_AGENT_SIGN_RESPONSE, sig
+
+
+class AgentTests(unittest.TestCase):
+ def _sign_with_agent(self, kwargs, expectation):
+ agent = ChaosAgent()
+ key = AgentKey(agent, b("secret!!!"))
+ result = key.sign_ssh_data(b("token"), **kwargs)
+ assert result == b("lol")
+ msg = agent._sent_message
+ msg.rewind()
+ assert msg.get_byte() == cSSH2_AGENTC_SIGN_REQUEST
+ assert msg.get_string() == b("secret!!!")
+ assert msg.get_string() == b("token")
+ assert msg.get_int() == expectation
+
+ def test_agent_signing_defaults_to_0_for_flags_field(self):
+ # No algorithm kwarg at all
+ self._sign_with_agent(kwargs=dict(), expectation=0)
+
+ def test_agent_signing_is_2_for_SHA256(self):
+ self._sign_with_agent(
+ kwargs=dict(algorithm="rsa-sha2-256"),
+ expectation=SSH_AGENT_RSA_SHA2_256,
+ )
+
+ def test_agent_signing_is_2_for_SHA512(self):
+ self._sign_with_agent(
+ kwargs=dict(algorithm="rsa-sha2-512"),
+ expectation=SSH_AGENT_RSA_SHA2_512,
+ )