Skip to content

Commit 2b66625

Browse files
committed
Add agent RSA-SHA2 support, also tweak changelog w/ more tickets
1 parent 363a28d commit 2b66625

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed

paramiko/agent.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@
4242
cSSH2_AGENTC_SIGN_REQUEST = byte_chr(13)
4343
SSH2_AGENT_SIGN_RESPONSE = 14
4444

45+
SSH_AGENT_RSA_SHA2_256 = 2
46+
SSH_AGENT_RSA_SHA2_512 = 4
47+
# NOTE: RFC mildly confusing; while these flags are OR'd together, OpenSSH at
48+
# least really treats them like "AND"s, in the sense that if it finds the
49+
# SHA256 flag set it won't continue looking at the SHA512 one; it
50+
# short-circuits right away.
51+
# Thus, we never want to eg submit 6 to say "either's good".
52+
ALGORITHM_FLAG_MAP = {
53+
"rsa-sha2-256": SSH_AGENT_RSA_SHA2_256,
54+
"rsa-sha2-512": SSH_AGENT_RSA_SHA2_512,
55+
}
56+
4557

4658
class AgentSSH(object):
4759
def __init__(self):
@@ -416,7 +428,7 @@ def sign_ssh_data(self, data, algorithm=None):
416428
msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
417429
msg.add_string(self.blob)
418430
msg.add_string(data)
419-
msg.add_int(0)
431+
msg.add_int(ALGORITHM_FLAG_MAP.get(algorithm, 0))
420432
ptype, result = self.agent._send_message(msg)
421433
if ptype != SSH2_AGENT_SIGN_RESPONSE:
422434
raise SSHException("key cannot be used for signing")

sites/www/changelog.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
Changelog
33
=========
44

5-
- :feature:`1643` Add support for SHA-2 variants of RSA key verification
6-
algorithms (as described in :rfc:`8332`) as well as limited SSH extension
7-
negotiation (:rfc:`8308`). How SSH servers/clients decide when and how to use
8-
this functionality can be complicated; Paramiko's support is as follows:
5+
- :feature:`1643` (also :issue:`1925`, :issue:`1644`, :issue:`1326`) Add
6+
support for SHA-2 variants of RSA key verification algorithms (as described
7+
in :rfc:`8332`) as well as limited SSH extension negotiation (:rfc:`8308`).
8+
How SSH servers/clients decide when and how to use this functionality can be
9+
complicated; Paramiko's support is as follows:
910

1011
- Client verification of server host key during key exchange will now prefer
1112
``rsa-sha2-512``, ``rsa-sha2-256``, and legacy ``ssh-rsa`` algorithms, in
@@ -35,6 +36,9 @@ Changelog
3536
supported by both ends is used, or if there is none, it falls back to the
3637
previous behavior.
3738

39+
- SSH agent support grew the ability to specify algorithm flags when
40+
requesting private key signatures; this is now used to forward SHA2
41+
algorithms when appropriate.
3842
- Server mode is now capable of pubkey auth involving SHA-2 signatures from
3943
clients, provided one's server implementation actually provides for doing
4044
so.

tests/test_agent.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import unittest
2+
3+
from paramiko.message import Message
4+
from paramiko.agent import (
5+
SSH2_AGENT_SIGN_RESPONSE,
6+
cSSH2_AGENTC_SIGN_REQUEST,
7+
SSH_AGENT_RSA_SHA2_256,
8+
SSH_AGENT_RSA_SHA2_512,
9+
AgentKey,
10+
)
11+
from paramiko.py3compat import b
12+
13+
14+
class ChaosAgent:
15+
def _send_message(self, msg):
16+
self._sent_message = msg
17+
sig = Message()
18+
sig.add_string(b("lol"))
19+
sig.rewind()
20+
return SSH2_AGENT_SIGN_RESPONSE, sig
21+
22+
23+
class AgentTests(unittest.TestCase):
24+
def _sign_with_agent(self, kwargs, expectation):
25+
agent = ChaosAgent()
26+
key = AgentKey(agent, b("secret!!!"))
27+
result = key.sign_ssh_data(b("token"), **kwargs)
28+
assert result == b("lol")
29+
msg = agent._sent_message
30+
msg.rewind()
31+
assert msg.get_byte() == cSSH2_AGENTC_SIGN_REQUEST
32+
assert msg.get_string() == b("secret!!!")
33+
assert msg.get_string() == b("token")
34+
assert msg.get_int() == expectation
35+
36+
def test_agent_signing_defaults_to_0_for_flags_field(self):
37+
# No algorithm kwarg at all
38+
self._sign_with_agent(kwargs=dict(), expectation=0)
39+
40+
def test_agent_signing_is_2_for_SHA256(self):
41+
self._sign_with_agent(
42+
kwargs=dict(algorithm="rsa-sha2-256"),
43+
expectation=SSH_AGENT_RSA_SHA2_256,
44+
)
45+
46+
def test_agent_signing_is_2_for_SHA512(self):
47+
self._sign_with_agent(
48+
kwargs=dict(algorithm="rsa-sha2-512"),
49+
expectation=SSH_AGENT_RSA_SHA2_512,
50+
)

0 commit comments

Comments
 (0)