为什么在密钥协商时首选密码、mac 和压缩被写入两次?

Why preferred ciphers, macs and compression are written twice while key negotiations?

我正在尝试了解 SSH 协议的客户端部分并参考 Paramiko library which is a native Python library for SSH protocol. The corresponding code can be found here

def _send_kex_init(self):
    """
    announce to the other side that we'd like to negotiate keys, and what
    kind of key negotiation we support.
    """
    self.clear_to_send_lock.acquire()
    try:
        self.clear_to_send.clear()
    finally:
        self.clear_to_send_lock.release()
    self.in_kex = True
    if self.server_mode:
        mp_required_prefix = 'diffie-hellman-group-exchange-sha'
        kex_mp = [k for k in self._preferred_kex if k.startswith(mp_required_prefix)]
        if (self._modulus_pack is None) and (len(kex_mp) > 0):
            # can't do group-exchange if we don't have a pack of potential primes
            pkex = [k for k in self.get_security_options().kex
                            if not k.startswith(mp_required_prefix)]
            self.get_security_options().kex = pkex
        available_server_keys = list(filter(list(self.server_key_dict.keys()).__contains__,
                                            self._preferred_keys))
    else:
        available_server_keys = self._preferred_keys

    m = Message()
    m.add_byte(cMSG_KEXINIT)
    m.add_bytes(os.urandom(16))
    m.add_list(self._preferred_kex)
    m.add_list(available_server_keys)
    m.add_list(self._preferred_ciphers)
    m.add_list(self._preferred_ciphers)
    m.add_list(self._preferred_macs)
    m.add_list(self._preferred_macs)
    m.add_list(self._preferred_compression)
    m.add_list(self._preferred_compression)
    m.add_string(bytes())
    m.add_string(bytes())
    m.add_boolean(False)
    m.add_int(0)
    # save a copy for later (needed to compute a hash)
    self.local_kex_init = m.asbytes()
    self._send_message(m)

SSH 协议允许双方对传入和传出方向实施不同的算法集。或者实际上,它允许一方仅针对其中一个方向实施特定算法。

Paramiko 实现了双向的所有算法,因此它使用相同的集合填充传入和传出算法的列表。


参见_parse_kex_init方法中的代码,它解析由您引用的代码填充的数据包:

    client_encrypt_algo_list = m.get_list()
    server_encrypt_algo_list = m.get_list()

...

        agreed_local_ciphers = list(
            filter(
                client_encrypt_algo_list.__contains__,
                self._preferred_ciphers,
            )
        )
        agreed_remote_ciphers = list(
            filter(
                server_encrypt_algo_list.__contains__,
                self._preferred_ciphers,
            )
        )