From a3f22a2cf9620de4e8d53ce3e7c9485c526eafce Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 8 Dec 2024 10:34:10 +0000 Subject: [PATCH] Use the new 'HYBRID' names for the hybrid KEX packets. draft-kampanakis-curdle-ssh-pq-ke defines the packet names SSH_MSG_KEX_HYBRID_INIT and SSH_MSG_KEX_HYBRID_REPLY. They have the same numbers as ECDH_INIT and ECDH_REPLY, and don't change anything else, so this is just a naming change. But I think it's a good one, because the post-quantum KEMs are less symmetric than ECDH (they're much more like Ben's RSA kex in concept, though very different in detail), and shouldn't try to pretend they're the same kind of thing. Also this enables logparse.pl to give a warning about the fact that one string in each packet contains two separate keys glomphed together. For the latter reason (and also because it's easier in my code structure) I've also switched to using the HYBRID naming for the existing NTRU + Curve25519 hybrid method, even though the Internet-Draft for that one still uses the ECDH names. Sorry, but I think it's clearer! --- contrib/logparse.pl | 40 ++++++++++++++++++++++++++++++++++++++++ crypto/ecc-ssh.c | 2 ++ crypto/kex-hybrid.c | 3 +++ ssh.h | 9 +++++++++ ssh/kex2-client.c | 2 +- 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/contrib/logparse.pl b/contrib/logparse.pl index eb429302..034aa72d 100755 --- a/contrib/logparse.pl +++ b/contrib/logparse.pl @@ -172,6 +172,16 @@ my %packets = ( my ($direction, $seq, $data) = @_; print "\n"; }, +#define SSH2_MSG_KEX_HYBRID_INIT 30 /* 0x1e */ + 'SSH2_MSG_KEX_HYBRID_INIT' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, +#define SSH2_MSG_KEX_HYBRID_REPLY 31 /* 0x1f */ + 'SSH2_MSG_KEX_HYBRID_REPLY' => sub { + my ($direction, $seq, $data) = @_; + print "\n"; + }, #define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */ 'SSH2_MSG_USERAUTH_REQUEST' => sub { my ($direction, $seq, $data) = @_; @@ -657,6 +667,16 @@ my %verbose_packet_dump_functions = ( # curve is. So the best we can do is just dump the raw data. printf " client public value: %s\n", (unpack "H*", $cpv); }, + 'SSH2_MSG_KEX_HYBRID_INIT' => sub { + my ($data) = @_; + my ($cpv) = &parse("s", $data); + # Hybrid post-quantum + classical KEX is even more confusing, + # since two separate pieces of data are glomphed together into + # this string without any obvious dividing line. The best we + # can sensibly do is to announce that in the log. + printf " client PQ encryption key + public ECDH value: %s\n", + (unpack "H*", $cpv); + }, 'SSH2_MSG_KEXDH_REPLY' => sub { my ($data) = @_; my ($hostkeyblob, $f, $sigblob) = &parse("sms", $data); @@ -708,6 +728,26 @@ my %verbose_packet_dump_functions = ( printf " $key: $value\n"; } }, + 'SSH2_MSG_KEX_HYBRID_REPLY' => sub { + my ($data) = @_; + my ($hostkeyblob, $spv, $sigblob) = &parse("sss", $data); + my ($hktype, @hostkey) = &parse_public_key($hostkeyblob); + printf " host key: %s\n", $hktype; + while (@hostkey) { + my ($key, $value) = splice @hostkey, 0, 2; + printf " $key: $value\n"; + } + # Similarly to HYBRID_INIT, warn the reader that this string + # contains two separate things glomphed together + printf " server PQ KEM ciphertext + public ECDH value: %s\n", + (unpack "H*", $spv); + printf " signature:\n"; + my @signature = &parse_signature($sigblob, $hktype); + while (@signature) { + my ($key, $value) = splice @signature, 0, 2; + printf " $key: $value\n"; + } + }, 'SSH2_MSG_NEWKEYS' => sub {}, 'SSH2_MSG_SERVICE_REQUEST' => sub { my ($data) = @_; diff --git a/crypto/ecc-ssh.c b/crypto/ecc-ssh.c index ca712c31..e524dfc4 100644 --- a/crypto/ecc-ssh.c +++ b/crypto/ecc-ssh.c @@ -1615,6 +1615,7 @@ static const ecdh_keyalg ssh_ecdhkex_m_alg = { .getpublic = ssh_ecdhkex_m_getpublic, .getkey = ssh_ecdhkex_m_getkey, .description = ssh_ecdhkex_description, + .packet_naming_ctx = SSH2_PKTCTX_ECDHKEX, }; const ssh_kex ssh_ec_kex_curve25519 = { .name = "curve25519-sha256", @@ -1655,6 +1656,7 @@ static const ecdh_keyalg ssh_ecdhkex_w_alg = { .getpublic = ssh_ecdhkex_w_getpublic, .getkey = ssh_ecdhkex_w_getkey, .description = ssh_ecdhkex_description, + .packet_naming_ctx = SSH2_PKTCTX_ECDHKEX, }; static const struct eckex_extra kex_extra_nistp256 = { ec_p256 }; const ssh_kex ssh_ec_kex_nistp256 = { diff --git a/crypto/kex-hybrid.c b/crypto/kex-hybrid.c index e7291342..e0c78743 100644 --- a/crypto/kex-hybrid.c +++ b/crypto/kex-hybrid.c @@ -169,6 +169,7 @@ static const ecdh_keyalg hybrid_client_vt = { .getpublic = hybrid_client_getpublic, .getkey = hybrid_client_getkey, .description = hybrid_description, + .packet_naming_ctx = SSH2_PKTCTX_HYBRIDKEX, }; /* ---------------------------------------------------------------------- @@ -267,6 +268,7 @@ static const ecdh_keyalg hybrid_server_vt = { .getkey = hybrid_server_getkey, .getpublic = hybrid_server_getpublic, .description = hybrid_description, + .packet_naming_ctx = SSH2_PKTCTX_HYBRIDKEX, }; /* ---------------------------------------------------------------------- @@ -287,6 +289,7 @@ static const ecdh_keyalg hybrid_selector_vt = { * functions that don't require an instance. */ .new = hybrid_selector_new, .description = hybrid_description, + .packet_naming_ctx = SSH2_PKTCTX_HYBRIDKEX, }; /* ---------------------------------------------------------------------- diff --git a/ssh.h b/ssh.h index dcd111a9..0dd3e6c0 100644 --- a/ssh.h +++ b/ssh.h @@ -136,6 +136,7 @@ typedef enum { SSH2_PKTCTX_DHGROUP, SSH2_PKTCTX_DHGEX, SSH2_PKTCTX_ECDHKEX, + SSH2_PKTCTX_HYBRIDKEX, SSH2_PKTCTX_GSSKEX, SSH2_PKTCTX_RSAKEX } Pkt_KCtx; @@ -992,6 +993,12 @@ struct ecdh_keyalg { void (*getpublic)(ecdh_key *key, BinarySink *bs); bool (*getkey)(ecdh_key *key, ptrlen remoteKey, BinarySink *bs); char *(*description)(const ssh_kex *kex); + + /* Some things that use this vtable are genuinely elliptic-curve + * Diffie-Hellman. Others are hybrid PQ + classical kex methods. + * Provide a packet-naming context for use in the SSH log. (Purely + * cosmetic.) */ + Pkt_KCtx packet_naming_ctx; }; static inline ecdh_key *ecdh_key_new(const ssh_kex *kex, bool is_server) { return kex->ecdh_vt->new(kex, is_server); } @@ -1788,6 +1795,8 @@ void platform_ssh_share_cleanup(const char *name); K(y, SSH2_MSG_KEXRSA_DONE, 32, SSH2_PKTCTX_RSAKEX) \ K(y, SSH2_MSG_KEX_ECDH_INIT, 30, SSH2_PKTCTX_ECDHKEX) \ K(y, SSH2_MSG_KEX_ECDH_REPLY, 31, SSH2_PKTCTX_ECDHKEX) \ + K(y, SSH2_MSG_KEX_HYBRID_INIT, 30, SSH2_PKTCTX_HYBRIDKEX) \ + K(y, SSH2_MSG_KEX_HYBRID_REPLY, 31, SSH2_PKTCTX_HYBRIDKEX) \ X(y, SSH2_MSG_USERAUTH_REQUEST, 50) \ X(y, SSH2_MSG_USERAUTH_FAILURE, 51) \ X(y, SSH2_MSG_USERAUTH_SUCCESS, 52) \ diff --git a/ssh/kex2-client.c b/ssh/kex2-client.c index d5425237..108cf549 100644 --- a/ssh/kex2-client.c +++ b/ssh/kex2-client.c @@ -190,7 +190,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) ssh_hash_alg(s->exhash)->text_name); sfree(desc); - s->ppl.bpp->pls->kctx = SSH2_PKTCTX_ECDHKEX; + s->ppl.bpp->pls->kctx = s->kex_alg->ecdh_vt->packet_naming_ctx; s->ecdh_key = ecdh_key_new(s->kex_alg, false);