Exemple #1
0
        public async Task <KeyExchangeOutput> TryExchangeAsync(SshConnection connection, KeyExchangeInput input, ILogger logger, CancellationToken ct)
        {
            var sequencePool = connection.SequencePool;

            using ECDiffieHellman ecdh = ECDiffieHellman.Create(_ecCurve);

            // Send ECDH_INIT.
            using ECDiffieHellmanPublicKey myPublicKey = ecdh.PublicKey;
            ECPoint q_c = myPublicKey.ExportParameters().Q;
            {
                using var ecdhInitMsg = CreateEcdhInitMessage(sequencePool, q_c);
                await connection.SendPacketAsync(ecdhInitMsg, ct);
            }

            // Receive ECDH_REPLY.
            Packet exchangeInitMsg = input.ExchangeInitMsg;

            using Packet exchangeInitMsgDispose =
                      exchangeInitMsg.IsEmpty ? (exchangeInitMsg = await connection.ReceivePacketAsync(ct)) : default(Packet);
            var ecdhReply = ParceEcdhReply(exchangeInitMsg);

            // TODO: Verify received key is valid.
            // TODO: Verify host key belongs to server.

            // Compute shared secret.
            // TODO: what types of exceptions can we get when creating the public key?
            ECParameters parameters = new ECParameters
            {
                Curve = _ecCurve,
                Q     = ecdhReply.q_s
            };

            using ECDiffieHellman peerEcdh = ECDiffieHellman.Create(parameters);
            using ECDiffieHellmanPublicKey peerPublicKey = peerEcdh.PublicKey;
            BigInteger sharedSecret = DeriveSharedSecret(ecdh, peerPublicKey);

            var publicHostKey = PublicKey.Read(ecdhReply.public_host_key, input.HostKeyAlgorithms);

            // Generate exchange hash.
            byte[] exchangeHash = CalculateExchangeHash(sequencePool, input.ConnectionInfo, input.ClientKexInitMsg, input.ServerKexInitMsg, ecdhReply.public_host_key, q_c, ecdhReply.q_s, sharedSecret);

            // Verify the server's signature.
            if (!publicHostKey.VerifySignature(exchangeHash, ecdhReply.exchange_hash_signature))
            {
                throw new KeyExchangeFailedException("Signature does not match host key.");
            }

            byte[] sessionId        = input.ConnectionInfo.SessionId ?? exchangeHash;
            byte[] initialIVC2S     = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'A', sessionId, input.InitialIVC2SLength);
            byte[] initialIVS2C     = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'B', sessionId, input.InitialIVS2CLength);
            byte[] encryptionKeyC2S = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'C', sessionId, input.EncryptionKeyC2SLength);
            byte[] encryptionKeyS2C = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'D', sessionId, input.EncryptionKeyS2CLength);
            byte[] integrityKeyC2S  = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'E', sessionId, input.IntegrityKeyC2SLength);
            byte[] integrityKeyS2C  = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'F', sessionId, input.IntegrityKeyS2CLength);

            return(new KeyExchangeOutput(exchangeHash,
                                         initialIVS2C, encryptionKeyS2C, integrityKeyS2C,
                                         initialIVC2S, encryptionKeyC2S, integrityKeyC2S));
        }
Exemple #2
0
        private async static Task PerformDefaultExchange(SshConnection connection, Packet clientKexInitMsg, Packet serverKexInitMsg, ILogger logger, SshClientSettings settings, SshConnectionInfo connectionInfo, CancellationToken ct)
        {
            // Key Exchange: https://tools.ietf.org/html/rfc4253#section-7.
            SequencePool sequencePool = connection.SequencePool;

            var remoteInit = ParseKeyExchangeInitMessage(serverKexInitMsg);

            // The chosen algorithm MUST be the first algorithm on the client's name-list
            // that is also on the server's name-list.
            Name encC2S = ChooseAlgorithm(settings.EncryptionAlgorithmsClientToServer, remoteInit.encryption_algorithms_client_to_server);
            Name encS2C = ChooseAlgorithm(settings.EncryptionAlgorithmsServerToClient, remoteInit.encryption_algorithms_server_to_client);
            Name macC2S = ChooseAlgorithm(settings.MacAlgorithmsClientToServer, remoteInit.mac_algorithms_client_to_server);
            Name macS2C = ChooseAlgorithm(settings.MacAlgorithmsServerToClient, remoteInit.mac_algorithms_server_to_client);
            Name comC2S = ChooseAlgorithm(settings.CompressionAlgorithmsClientToServer, remoteInit.compression_algorithms_client_to_server);
            Name comS2C = ChooseAlgorithm(settings.CompressionAlgorithmsServerToClient, remoteInit.compression_algorithms_server_to_client);

            if (encC2S.IsEmpty || encS2C.IsEmpty || macC2S.IsEmpty || macS2C.IsEmpty || comC2S.IsEmpty || comS2C.IsEmpty)
            {
                ThrowHelper.ThrowKeyExchangeFailed("No common encryption/integrity/compression algorithm.");
            }

            // Make an ordered list of host key algorithms. The key exchange algorithm will pick a compatible one.
            List <Name> hostKeyAlgorithms = new List <Name>(capacity: settings.ServerHostKeyAlgorithms.Count);

            foreach (var hostKeyAlgorithm in settings.ServerHostKeyAlgorithms)
            {
                if (remoteInit.server_host_key_algorithms.Contains(hostKeyAlgorithm))
                {
                    hostKeyAlgorithms.Add(hostKeyAlgorithm);
                }
            }

            // The first algorithm MUST be the preferred (and guessed) algorithm.  If
            // both sides make the same guess, that algorithm MUST be used.
            Name matchingKex = settings.KeyExchangeAlgorithms.Count == 0 ||
                               remoteInit.kex_algorithms.Length == 0 ||
                               settings.KeyExchangeAlgorithms[0] != remoteInit.kex_algorithms[0] ? default(Name) : settings.KeyExchangeAlgorithms[0];

            KeyExchangeOutput?keyExchangeOutput = null;
            Packet            exchangeInitMsg   = default;

            try
            {
                if (remoteInit.first_kex_packet_follows)
                {
                    exchangeInitMsg = await connection.ReceivePacketAsync(ct);

                    if (matchingKex.IsEmpty ||
                        settings.ServerHostKeyAlgorithms.Count == 0 ||
                        remoteInit.server_host_key_algorithms.Length == 0 ||
                        settings.ServerHostKeyAlgorithms[0] != remoteInit.server_host_key_algorithms[0])
                    {
                        // Silently ignore if guessed wrong.
                        exchangeInitMsg.Dispose();
                        exchangeInitMsg = default;
                    }
                    else
                    {
                        // Only accept the first hostKeyAlgorithm.
                        if (hostKeyAlgorithms.Count > 1)
                        {
                            hostKeyAlgorithms.RemoveRange(1, hostKeyAlgorithms.Count - 1);
                        }
                    }
                }

                EncryptionFactory.Default.GetKeyAndIVLength(encC2S, out int encryptionKeyC2SLength, out int initialIVC2SLength);
                EncryptionFactory.Default.GetKeyAndIVLength(encS2C, out int encryptionKeyS2CLength, out int initialIVS2CLength);
                int integrityKeyC2SLength = HMacFactory.Default.GetKeyLength(macC2S);
                int integrityKeyS2CLength = HMacFactory.Default.GetKeyLength(macS2C);

                var keyExchangeInput = new KeyExchangeInput(hostKeyAlgorithms, exchangeInitMsg, clientKexInitMsg, serverKexInitMsg, connectionInfo,
                                                            initialIVC2SLength, initialIVS2CLength, encryptionKeyC2SLength, encryptionKeyS2CLength, integrityKeyC2SLength, integrityKeyS2CLength);

                foreach (var keyAlgorithm in settings.KeyExchangeAlgorithms)
                {
                    if (remoteInit.kex_algorithms.Contains(keyAlgorithm))
                    {
                        logger.KeyExchangeAlgorithm(keyAlgorithm);

                        using (var algorithm = KeyExchangeAlgorithmFactory.Default.Create(keyAlgorithm))
                        {
                            keyExchangeOutput = await algorithm.TryExchangeAsync(connection, keyExchangeInput, logger, ct);
                        }
                        if (keyExchangeOutput != null)
                        {
                            connectionInfo.SessionId ??= keyExchangeOutput.ExchangeHash;
                            break;
                        }

                        // Preferred algorithm must be used.
                        if (!matchingKex.IsEmpty)
                        {
                            ThrowHelper.ThrowKeyExchangeFailed("Preferred key exchange algorithm failed.");
                        }
                    }
                }

                // If no algorithm satisfying all these conditions can be found, the
                // connection fails, and both sides MUST disconnect.
                if (keyExchangeOutput == null)
                {
                    ThrowHelper.ThrowKeyExchangeFailed("Key exchange failed");
                }
            }
            finally
            {
                exchangeInitMsg.Dispose();
            }

            logger.AlgorithmsServerToClient(encS2C, macS2C, comS2C);
            logger.AlgorithmsClientToServer(encC2S, macC2S, comC2S);

            // Send SSH_MSG_NEWKEYS.
            {
                using Packet newKeysMsg = CreateNewKeysMessage(sequencePool);
                await connection.SendPacketAsync(newKeysMsg, ct);
            }

            // Receive SSH_MSG_NEWKEYS.
            using Packet newKeysReceivedMsg = await connection.ReceivePacketAsync(ct);

            ParseNewKeysMessage(newKeysReceivedMsg);

            var encrypt       = EncryptionFactory.Default.CreateEncryptor(encC2S, keyExchangeOutput.EncryptionKeyC2S, keyExchangeOutput.InitialIVC2S);
            var macForEncoder = HMacFactory.Default.Create(macC2S, keyExchangeOutput.IntegrityKeyC2S);
            var decrypt       = EncryptionFactory.Default.CreateDecryptor(encS2C, keyExchangeOutput.EncryptionKeyS2C, keyExchangeOutput.InitialIVS2C);
            var macForDecoder = HMacFactory.Default.Create(macS2C, keyExchangeOutput.IntegrityKeyS2C);

            connection.SetEncoderDecoder(new PacketEncoder(encrypt, macForEncoder), new PacketDecoder(sequencePool, decrypt, macForDecoder));