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);