diff options
-rw-r--r-- | paramiko/agent.py | 14 | ||||
-rw-r--r-- | sites/www/changelog.rst | 12 | ||||
-rw-r--r-- | tests/test_agent.py | 50 |
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, + ) |