Description: Backport upsteam fix for QTCREATORBUG-13340 
"SSH Client Does Not Support OpenSSH 6.7 Ciphers"
SSH: Add support for the recommended cipher modes from RFC 4344.
.
Author: Benjamin Zeller <benjamin.zeller@canonical.com>
Reviewed-By: 
Last-Update: <2014-11-10>
--- a/src/libs/ssh/sshbotanconversions_p.h
+++ b/src/libs/ssh/sshbotanconversions_p.h
@@ -31,6 +31,7 @@
 #define SSHBOTANCONVERSIONS_P_H
 
 #include "sshcapabilities_p.h"
+#include "sshexception_p.h"
 
 #include <botan/botan.h>
 
@@ -62,10 +63,22 @@
 
 inline const char *botanCryptAlgoName(const QByteArray &rfcAlgoName)
 {
-    Q_ASSERT(rfcAlgoName == SshCapabilities::CryptAlgo3Des
-        || rfcAlgoName == SshCapabilities::CryptAlgoAes128);
-    return rfcAlgoName == SshCapabilities::CryptAlgo3Des
-        ? "TripleDES" : "AES-128";
+    if (rfcAlgoName == SshCapabilities::CryptAlgoAes128Cbc
+            || rfcAlgoName == SshCapabilities::CryptAlgoAes128Ctr) {
+        return "AES-128";
+    }
+    if (rfcAlgoName == SshCapabilities::CryptAlgo3DesCbc
+            || rfcAlgoName == SshCapabilities::CryptAlgo3DesCtr) {
+        return "TripleDES";
+    }
+    if (rfcAlgoName == SshCapabilities::CryptAlgoAes192Ctr) {
+        return "AES-192";
+    }
+    if (rfcAlgoName == SshCapabilities::CryptAlgoAes256Ctr) {
+        return "AES-256";
+    }
+    throw SshClientException(SshInternalError, SSH_TR("Unexpected cipher \"%1\"")
+                             .arg(QString::fromLatin1(rfcAlgoName)));
 }
 
 inline const char *botanEmsaAlgoName(const QByteArray &rfcAlgoName)
--- a/src/libs/ssh/sshcapabilities.cpp
+++ b/src/libs/ssh/sshcapabilities.cpp
@@ -61,11 +61,19 @@
     = QList<QByteArray>() << SshCapabilities::PubKeyRsa
           << SshCapabilities::PubKeyDss;
 
-const QByteArray SshCapabilities::CryptAlgo3Des("3des-cbc");
-const QByteArray SshCapabilities::CryptAlgoAes128("aes128-cbc");
+const QByteArray SshCapabilities::CryptAlgo3DesCbc("3des-cbc");
+const QByteArray SshCapabilities::CryptAlgo3DesCtr("3des-ctr");
+const QByteArray SshCapabilities::CryptAlgoAes128Cbc("aes128-cbc");
+const QByteArray SshCapabilities::CryptAlgoAes128Ctr("aes128-ctr");
+const QByteArray SshCapabilities::CryptAlgoAes192Ctr("aes192-ctr");
+const QByteArray SshCapabilities::CryptAlgoAes256Ctr("aes256-ctr");
 const QList<QByteArray> SshCapabilities::EncryptionAlgorithms
-    = QList<QByteArray>() << SshCapabilities::CryptAlgoAes128
-          << SshCapabilities::CryptAlgo3Des;
+    = QList<QByteArray>() << SshCapabilities::CryptAlgoAes256Ctr
+                          << SshCapabilities::CryptAlgoAes192Ctr
+                          << SshCapabilities::CryptAlgoAes128Ctr
+                          << SshCapabilities::CryptAlgo3DesCtr
+                          << SshCapabilities::CryptAlgoAes128Cbc
+                          << SshCapabilities::CryptAlgo3DesCbc;
 
 const QByteArray SshCapabilities::HMacSha1("hmac-sha1");
 const QByteArray SshCapabilities::HMacSha196("hmac-sha1-96");
--- a/src/libs/ssh/sshcapabilities_p.h
+++ b/src/libs/ssh/sshcapabilities_p.h
@@ -47,8 +47,12 @@
     static const QByteArray PubKeyRsa;
     static const QList<QByteArray> PublicKeyAlgorithms;
 
-    static const QByteArray CryptAlgo3Des;
-    static const QByteArray CryptAlgoAes128;
+    static const QByteArray CryptAlgo3DesCbc;
+    static const QByteArray CryptAlgo3DesCtr;
+    static const QByteArray CryptAlgoAes128Cbc;
+    static const QByteArray CryptAlgoAes128Ctr;
+    static const QByteArray CryptAlgoAes192Ctr;
+    static const QByteArray CryptAlgoAes256Ctr;
     static const QList<QByteArray> EncryptionAlgorithms;
 
     static const QByteArray HMacSha1;
--- a/src/libs/ssh/sshchannel.cpp
+++ b/src/libs/ssh/sshchannel.cpp
@@ -39,7 +39,9 @@
 namespace QSsh {
 namespace Internal {
 
-const quint32 MinMaxPacketSize = 32768;
+// "Payload length" (RFC 4253, 6.1), i.e. minus packet type, channel number
+// and length field for string.
+const quint32 MinMaxPacketSize = 32768 - sizeof(quint32) - sizeof(quint32) - 1;
 const quint32 NoChannel = 0xffffffffu;
 
 AbstractSshChannel::AbstractSshChannel(quint32 channelId,
--- a/src/libs/ssh/sshcryptofacility.cpp
+++ b/src/libs/ssh/sshcryptofacility.cpp
@@ -42,6 +42,7 @@
 #include <botan/pkcs8.h>
 #include <botan/pubkey.h>
 #include <botan/rsa.h>
+#include <botan/ctr.h>
 
 #include <QDebug>
 #include <QList>
@@ -69,6 +70,16 @@
     m_hMac.reset(0);
 }
 
+SshAbstractCryptoFacility::Mode SshAbstractCryptoFacility::getMode(const QByteArray &algoName)
+{
+    if (algoName.endsWith("-ctr"))
+        return CtrMode;
+    if (algoName.endsWith("-cbc"))
+        return CbcMode;
+    throw SshClientException(SshInternalError, SSH_TR("Unexpected cipher \"%1\"")
+                             .arg(QString::fromLatin1(algoName)));
+}
+
 void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
 {
     checkInvariant();
@@ -76,8 +87,9 @@
     if (m_sessionId.isEmpty())
         m_sessionId = kex.h();
     Algorithm_Factory &af = global_state().algorithm_factory();
-    const std::string &cryptAlgo = botanCryptAlgoName(cryptAlgoName(kex));
-    BlockCipher * const cipher = af.prototype_block_cipher(cryptAlgo)->clone();
+    const QByteArray &rfcCryptAlgoName = cryptAlgoName(kex);
+    BlockCipher * const cipher
+            = af.prototype_block_cipher(botanCryptAlgoName(rfcCryptAlgoName))->clone();
 
     m_cipherBlockSize = cipher->block_size();
     const QByteArray ivData = generateHash(kex, ivChar(), m_cipherBlockSize);
@@ -86,8 +98,8 @@
     const quint32 keySize = cipher->key_spec().maximum_keylength();
     const QByteArray cryptKeyData = generateHash(kex, keyChar(), keySize);
     SymmetricKey cryptKey(convertByteArray(cryptKeyData), keySize);
-
-    Keyed_Filter * const cipherMode = makeCipherMode(cipher, new Null_Padding, iv, cryptKey);
+    Keyed_Filter * const cipherMode
+            = makeCipherMode(cipher, getMode(rfcCryptAlgoName), iv, cryptKey);
     m_pipe.reset(new Pipe(cipherMode));
 
     m_macLength = botanHMacKeyLen(hMacAlgoName(kex));
@@ -123,6 +135,15 @@
     }
 }
 
+Keyed_Filter *SshAbstractCryptoFacility::makeCtrCipherMode(BlockCipher *cipher,
+        const InitializationVector &iv, const SymmetricKey &key)
+{
+    StreamCipher_Filter * const filter = new StreamCipher_Filter(new CTR_BE(cipher));
+    filter->set_key(key);
+    filter->set_iv(iv);
+    return filter;
+}
+
 QByteArray SshAbstractCryptoFacility::generateMac(const QByteArray &data,
     quint32 dataSize) const
 {
@@ -172,11 +193,16 @@
     return kex.hMacAlgoClientToServer();
 }
 
-Keyed_Filter *SshEncryptionFacility::makeCipherMode(BlockCipher *cipher,
-    BlockCipherModePaddingMethod *paddingMethod, const InitializationVector &iv,
-    const SymmetricKey &key)
+Keyed_Filter *SshEncryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode,
+        const InitializationVector &iv, const SymmetricKey &key)
 {
-    return new CBC_Encryption(cipher, paddingMethod, key, iv);
+    switch (mode) {
+    case CbcMode:
+        return new CBC_Encryption(cipher, new Null_Padding, key, iv);
+    case CtrMode:
+        return makeCtrCipherMode(cipher, iv, key);
+    }
+    return 0; // For dumb compilers.
 }
 
 void SshEncryptionFacility::encrypt(QByteArray &data) const
@@ -364,11 +390,16 @@
     return kex.hMacAlgoServerToClient();
 }
 
-Keyed_Filter *SshDecryptionFacility::makeCipherMode(BlockCipher *cipher,
-    BlockCipherModePaddingMethod *paddingMethod, const InitializationVector &iv,
+Keyed_Filter *SshDecryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode, const InitializationVector &iv,
     const SymmetricKey &key)
 {
-    return new CBC_Decryption(cipher, paddingMethod, key, iv);
+    switch (mode) {
+    case CbcMode:
+        return new CBC_Decryption(cipher, new Null_Padding, key, iv);
+    case CtrMode:
+        return makeCtrCipherMode(cipher, iv, key);
+    }
+    return 0; // For dumb compilers.
 }
 
 void SshDecryptionFacility::decrypt(QByteArray &data, quint32 offset,
--- a/src/libs/ssh/sshcryptofacility_p.h
+++ b/src/libs/ssh/sshcryptofacility_p.h
@@ -54,9 +54,13 @@
     quint32 macLength() const { return m_macLength; }
 
 protected:
+    enum Mode { CbcMode, CtrMode };
+
     SshAbstractCryptoFacility();
     void convert(QByteArray &data, quint32 offset, quint32 dataSize) const;
     QByteArray sessionId() const { return m_sessionId; }
+    Botan::Keyed_Filter *makeCtrCipherMode(Botan::BlockCipher *cipher,
+        const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
 
 private:
     SshAbstractCryptoFacility(const SshAbstractCryptoFacility &);
@@ -65,15 +69,14 @@
     virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const = 0;
     virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const = 0;
     virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
-        Botan::BlockCipherModePaddingMethod *paddingMethod,
-        const Botan::InitializationVector &iv,
-        const Botan::SymmetricKey &key) = 0;
+        Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key) = 0;
     virtual char ivChar() const = 0;
     virtual char keyChar() const = 0;
     virtual char macChar() const = 0;
 
     QByteArray generateHash(const SshKeyExchange &kex, char c, quint32 length);
     void checkInvariant() const;
+    static Mode getMode(const QByteArray &algoName);
 
     QByteArray m_sessionId;
     QScopedPointer<Botan::Pipe> m_pipe;
@@ -99,8 +102,7 @@
     virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
     virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
     virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
-        Botan::BlockCipherModePaddingMethod *paddingMethod,
-        const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
+            Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
     virtual char ivChar() const { return 'A'; }
     virtual char keyChar() const { return 'C'; }
     virtual char macChar() const { return 'E'; }
@@ -131,8 +133,7 @@
     virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
     virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
     virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
-        Botan::BlockCipherModePaddingMethod *paddingMethod,
-        const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
+            Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
     virtual char ivChar() const { return 'B'; }
     virtual char keyChar() const { return 'D'; }
     virtual char macChar() const { return 'F'; }
