SSH handshake in simple words.

Secure Shell (SSH) is a widely used transport layer protocol for securing connections between clients and servers. This is the basic protocol in our Teleport program for secure access to infrastructure. Below is a relatively brief description of the handshake that occurs before the establishment of a secure channel between the client and server and before the full encryption of traffic begins.



Version sharing



The handshake begins with the fact that both sides send each other a string with the version number. Nothing extremely exciting happens in this part of the handshake, but it should be noted that most relatively modern clients and servers only support SSH 2.0 due to design flaws in version 1.0.



Key exchange



During the key exchange process (sometimes called KEX), the parties exchange publicly available information and derive a secret shared by the client and server. This secret cannot be discovered or obtained from publicly available information.



Key Exchange Initialization



Key exchange begins with the fact that both parties send each other a message SSH_MSG_KEX_INIT



with a list of supported cryptographic primitives and their preferred order.



Cryptographic primitives must establish the building blocks that will be used for key exchange, and then complete data encryption. The table below lists the cryptographic primitives that Teleport supports.



Key Exchange (KEX) Symmetric cipher Message Authentication Code (MAC) Server Host Key Algorithm
curve25519-sha256@libssh.org chacha20-poly1305@openssh.com hmac-sha2-256-etm@openssh.com ssh-rsa-cert-v01@openssh.com
ecdh-sha2-nistp256 aes128-gcm@openssh.com hmac-sha2-256 ssh-rsa
ecdh-sha2-nistp384 aes256-ctr
ecdh-sha2-nistp521 aes192-ctr
aes128-ctr
Teleport default cryptographic primitives



Initialization of the Diffie-Hellman Protocol on Elliptic Curves



Since both sides use the same algorithm to select cryptographic primitives from the list of supported ones, after initialization, you can immediately begin exchanging keys. Teleport only supports the Elliptic Curve Diffie-Hellman (ECDH) protocol, so key exchange begins with the client generating an ephemeral key pair (private and associated public key) and sending the server its public key in the message SSH_MSG_KEX_ECDH_INIT



.



It is worth emphasizing that this key pair is ephemeral: it is used only for key exchange, and then it will be deleted. This makes it extremely difficult to conduct a class of attacks where an attacker passively records encrypted traffic with the hope of stealing the private key sometime in the future (as stipulated by Yarovaya’s law - approx. Trans.). It is very difficult to steal something that no longer exists. This property is called forward secrecy.







Fig. 1. Generating a key exchange initialization message



Diffie-Hellman response on elliptic curves



The server waits for the SSH_MSG_KEX_ECDH_INIT



message, and upon receipt, generates its own ephemeral key pair. Using the client’s public key and its own key pair, the server can generate a shared secret K.



Then the server generates something called the exchange hash H and signs it, generating a signed HS hash (more on Fig. 3). The exchange hash and its signature serve several purposes:





The exchange hash is generated by taking the hash (SHA256, SHA384 or SHA512, depending on the key exchange algorithm) of the following fields:





With this information, the server can construct the SSH_MSG_KEX_ECDH_REPLY



message using the ephemeral public key of server B



, the public key of the HPub



server host, and the signature on the HS



exchange hash. See fig. 4 for more details.





Fig. 2. Generation of the exchange hash H



As soon as the client received SSH_MSG_KEX_ECDH_REPLY



from the server, it has everything necessary to calculate the secret K



and the exchange hash H







In the last part of the key exchange, the client retrieves the host public key (or certificate) from SSH_MSG_KEX_ECDH_REPLY



and verifies the signature of the HS



exchange hash confirming ownership of the private key of the host. To prevent attacks of the “man in the middle” type (MitM), after checking the signature, the host’s public key (or certificate) is checked against the local database of known hosts; if this key (or certificate) is not trusted, the connection is disconnected.



  The authenticity of host 10.10.10.10 (10.10.10.10) 'can't be established.
 ECDSA key fingerprint is SHA256: pnPn3SxExHtVGNdzbV0cRzUrtNhqZv + Pwdq / qGQPZO3.
 Are you sure you want to continue connecting (yes / no)? 
The SSH client offers to add the host key to the local database of known hosts. For OpenSSH, this is usually ~/.ssh/known_hosts







Such a message means that the key presented is not in your local database of known hosts. A good way to avoid such messages is to use SSH certificates (which Teleport does by default) instead of keys, which allows you to simply store the certificate of the certification authority in a local database of known hosts, and then check all the hosts signed by this CA.





Fig. 3. Generating an ECDH key exchange response



New keys



Before starting mass data encryption, the last nuance remained. Both parties must create six keys: two for encryption, two initialization vectors (IV) and two for integrity. You may ask, why are there so many extra keys? Isn't K secret enough? No, not enough.



First, why do we need separate keys for encryption, integrity and IV. One of the reasons is related to the historical development of protocols such as TLS and SSH, namely, the negotiation of cryptographic primitives. In some selected cryptographic primitives, key reuse is not a problem. But, as Henryk Hellstrom correctly explains , if the primitives are incorrectly selected (for example, AES-256-CBC for encryption and AES-256-CBC-MAC for authentication), the consequences can be disastrous. It should be noted that protocol developers are gradually abandoning such flexibility to make protocols simpler and more secure.



Next, why are keys of each type used.



Encryption keys ensure data confidentiality and are used with a symmetric cipher to encrypt and decrypt a message.



Integrity keys are commonly used with Message Authentication Code (MAC) to ensure the authenticity of the ciphertext. In the absence of integrity checks, an attacker can modify the ciphertext that is transmitted over open channels and you will decrypt a fake message. This scheme is usually called Encrypt-then-MAC .



It should be noted that modern AEAD ciphers (authenticated encryption with attached data, when part of the message is encrypted, part remains open and the whole message is authenticated) like aes128-gcm@openssh.com



and chacha20-poly1305@openssh.com



do not actually use the derived key integrity for the MAC, and perform authentication within their structure.



Initialization vectors (IV) are usually random numbers used as input for a symmetric cipher. Their goal is to ensure that the same message, encrypted twice, does not lead to the same ciphertext. The need for such a procedure is perfectly demonstrated by the famous Tux penguin image, encrypted in electronic code book (ECB) mode.





From left to right. (1) Clear text as an image. (2) A cryptogram obtained by encryption in ECB mode. (3) A cryptogram obtained by encryption in a mode other than ECB. The image is a pseudo-random pixel sequence



Using (and hacking) IV vectors is an interesting topic in itself, which Filippo Walsord wrote about .



Finally, why do keys come in pairs? As Thomas Pornin noted, if only one integrity key is used, the attacker can reproduce the record sent to him to the client, and he will consider it valid. With paired integrity keys (on the server and the client), the client will check the integrity of the ciphertext and this trick will not work.



Now with an understanding of why these keys are needed, let's see how they are generated according to the RFC :



  • Starting vector IV from client to server: HASH(K || H || «A» || session_id)





  • Starting vector IV from server to client: HASH(K || H || «B» || session_id)





  • Encryption key from client to server: HASH(K || H || «C» || session_id)





  • Encryption key from server to client: HASH(K || H || «D» || session_id)





  • Integrity control key from client to server: HASH(K || H || «E» || session_id)





  • Integrity control key from server to client: HASH(K || H || «F» || session_id)





Here the SHA hash algorithm is used {256, 384 or 512} depending on the key exchange algorithm, and the symbol || implies concatenation, i.e. traction.



As soon as these values ​​are calculated, both sides send SSH_MSG_NEWKEYS



to inform the other side that the key exchange is complete and all future communications should be carried out using the new keys created above.





Fig. 4:. Initial vector generation IV. Generation for other keys occurs according to the same scheme, if we replace A and B with C, D, E and F, respectively



Conclusion



At this stage, both parties agreed on cryptographic primitives, exchanged secrets and generated key material for the selected primitives. Now, a secure channel can be established between the client and the server, which will ensure confidentiality and integrity.



This is how SSH handshakes establish a secure connection between clients and servers.



All Articles