SSH: Add support for the recommended cipher modes from RFC 4344.

New OpenSSH versions do not support the (required) CBC modes out of the
box anymore, so let's add some CTR ones.

Task-number: QTCREATORBUG-13340
Change-Id: Ia3e38be3aab95be258e64396283736d246c8b93b
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Reviewed-by: Christian Kandeler <christian.kandeler@digia.com>
This commit is contained in:
Christian Kandeler
2014-11-06 11:40:00 +01:00
parent c0d6c7c54a
commit 45de9ee23f
5 changed files with 84 additions and 28 deletions

View File

@@ -32,6 +32,7 @@
#define SSHBOTANCONVERSIONS_P_H #define SSHBOTANCONVERSIONS_P_H
#include "sshcapabilities_p.h" #include "sshcapabilities_p.h"
#include "sshexception_p.h"
#include <botan/botan.h> #include <botan/botan.h>
@@ -63,10 +64,22 @@ inline const char *botanKeyExchangeAlgoName(const QByteArray &rfcAlgoName)
inline const char *botanCryptAlgoName(const QByteArray &rfcAlgoName) inline const char *botanCryptAlgoName(const QByteArray &rfcAlgoName)
{ {
Q_ASSERT(rfcAlgoName == SshCapabilities::CryptAlgo3Des if (rfcAlgoName == SshCapabilities::CryptAlgoAes128Cbc
|| rfcAlgoName == SshCapabilities::CryptAlgoAes128); || rfcAlgoName == SshCapabilities::CryptAlgoAes128Ctr) {
return rfcAlgoName == SshCapabilities::CryptAlgo3Des return "AES-128";
? "TripleDES" : "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) inline const char *botanEmsaAlgoName(const QByteArray &rfcAlgoName)

View File

@@ -62,11 +62,19 @@ const QList<QByteArray> SshCapabilities::PublicKeyAlgorithms
= QList<QByteArray>() << SshCapabilities::PubKeyRsa = QList<QByteArray>() << SshCapabilities::PubKeyRsa
<< SshCapabilities::PubKeyDss; << SshCapabilities::PubKeyDss;
const QByteArray SshCapabilities::CryptAlgo3Des("3des-cbc"); const QByteArray SshCapabilities::CryptAlgo3DesCbc("3des-cbc");
const QByteArray SshCapabilities::CryptAlgoAes128("aes128-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 const QList<QByteArray> SshCapabilities::EncryptionAlgorithms
= QList<QByteArray>() << SshCapabilities::CryptAlgoAes128 = QList<QByteArray>() << SshCapabilities::CryptAlgoAes256Ctr
<< SshCapabilities::CryptAlgo3Des; << SshCapabilities::CryptAlgoAes192Ctr
<< SshCapabilities::CryptAlgoAes128Ctr
<< SshCapabilities::CryptAlgo3DesCtr
<< SshCapabilities::CryptAlgoAes128Cbc
<< SshCapabilities::CryptAlgo3DesCbc;
const QByteArray SshCapabilities::HMacSha1("hmac-sha1"); const QByteArray SshCapabilities::HMacSha1("hmac-sha1");
const QByteArray SshCapabilities::HMacSha196("hmac-sha1-96"); const QByteArray SshCapabilities::HMacSha196("hmac-sha1-96");

View File

@@ -48,8 +48,12 @@ public:
static const QByteArray PubKeyRsa; static const QByteArray PubKeyRsa;
static const QList<QByteArray> PublicKeyAlgorithms; static const QList<QByteArray> PublicKeyAlgorithms;
static const QByteArray CryptAlgo3Des; static const QByteArray CryptAlgo3DesCbc;
static const QByteArray CryptAlgoAes128; 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 QList<QByteArray> EncryptionAlgorithms;
static const QByteArray HMacSha1; static const QByteArray HMacSha1;

View File

@@ -65,6 +65,16 @@ void SshAbstractCryptoFacility::clearKeys()
m_hMac.reset(0); 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) void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
{ {
checkInvariant(); checkInvariant();
@@ -72,8 +82,9 @@ void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
if (m_sessionId.isEmpty()) if (m_sessionId.isEmpty())
m_sessionId = kex.h(); m_sessionId = kex.h();
Algorithm_Factory &af = global_state().algorithm_factory(); Algorithm_Factory &af = global_state().algorithm_factory();
const std::string &cryptAlgo = botanCryptAlgoName(cryptAlgoName(kex)); const QByteArray &rfcCryptAlgoName = cryptAlgoName(kex);
BlockCipher * const cipher = af.prototype_block_cipher(cryptAlgo)->clone(); BlockCipher * const cipher
= af.prototype_block_cipher(botanCryptAlgoName(rfcCryptAlgoName))->clone();
m_cipherBlockSize = cipher->block_size(); m_cipherBlockSize = cipher->block_size();
const QByteArray ivData = generateHash(kex, ivChar(), m_cipherBlockSize); const QByteArray ivData = generateHash(kex, ivChar(), m_cipherBlockSize);
@@ -82,8 +93,8 @@ void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
const quint32 keySize = cipher->key_spec().maximum_keylength(); const quint32 keySize = cipher->key_spec().maximum_keylength();
const QByteArray cryptKeyData = generateHash(kex, keyChar(), keySize); const QByteArray cryptKeyData = generateHash(kex, keyChar(), keySize);
SymmetricKey cryptKey(convertByteArray(cryptKeyData), keySize); SymmetricKey cryptKey(convertByteArray(cryptKeyData), keySize);
Keyed_Filter * const cipherMode
Keyed_Filter * const cipherMode = makeCipherMode(cipher, new Null_Padding, iv, cryptKey); = makeCipherMode(cipher, getMode(rfcCryptAlgoName), iv, cryptKey);
m_pipe.reset(new Pipe(cipherMode)); m_pipe.reset(new Pipe(cipherMode));
m_macLength = botanHMacKeyLen(hMacAlgoName(kex)); m_macLength = botanHMacKeyLen(hMacAlgoName(kex));
@@ -119,6 +130,15 @@ void SshAbstractCryptoFacility::convert(QByteArray &data, quint32 offset,
} }
} }
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, QByteArray SshAbstractCryptoFacility::generateMac(const QByteArray &data,
quint32 dataSize) const quint32 dataSize) const
{ {
@@ -168,11 +188,16 @@ QByteArray SshEncryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
return kex.hMacAlgoClientToServer(); return kex.hMacAlgoClientToServer();
} }
Keyed_Filter *SshEncryptionFacility::makeCipherMode(BlockCipher *cipher, Keyed_Filter *SshEncryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode,
BlockCipherModePaddingMethod *paddingMethod, const InitializationVector &iv, const InitializationVector &iv, const SymmetricKey &key)
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 void SshEncryptionFacility::encrypt(QByteArray &data) const
@@ -360,11 +385,16 @@ QByteArray SshDecryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
return kex.hMacAlgoServerToClient(); return kex.hMacAlgoServerToClient();
} }
Keyed_Filter *SshDecryptionFacility::makeCipherMode(BlockCipher *cipher, Keyed_Filter *SshDecryptionFacility::makeCipherMode(BlockCipher *cipher, Mode mode, const InitializationVector &iv,
BlockCipherModePaddingMethod *paddingMethod, const InitializationVector &iv,
const SymmetricKey &key) 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, void SshDecryptionFacility::decrypt(QByteArray &data, quint32 offset,

View File

@@ -53,9 +53,13 @@ public:
quint32 macLength() const { return m_macLength; } quint32 macLength() const { return m_macLength; }
protected: protected:
enum Mode { CbcMode, CtrMode };
SshAbstractCryptoFacility(); SshAbstractCryptoFacility();
void convert(QByteArray &data, quint32 offset, quint32 dataSize) const; void convert(QByteArray &data, quint32 offset, quint32 dataSize) const;
QByteArray sessionId() const { return m_sessionId; } QByteArray sessionId() const { return m_sessionId; }
Botan::Keyed_Filter *makeCtrCipherMode(Botan::BlockCipher *cipher,
const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
private: private:
SshAbstractCryptoFacility(const SshAbstractCryptoFacility &); SshAbstractCryptoFacility(const SshAbstractCryptoFacility &);
@@ -64,15 +68,14 @@ private:
virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const = 0; virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const = 0;
virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const = 0; virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const = 0;
virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher, virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
Botan::BlockCipherModePaddingMethod *paddingMethod, Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key) = 0;
const Botan::InitializationVector &iv,
const Botan::SymmetricKey &key) = 0;
virtual char ivChar() const = 0; virtual char ivChar() const = 0;
virtual char keyChar() const = 0; virtual char keyChar() const = 0;
virtual char macChar() const = 0; virtual char macChar() const = 0;
QByteArray generateHash(const SshKeyExchange &kex, char c, quint32 length); QByteArray generateHash(const SshKeyExchange &kex, char c, quint32 length);
void checkInvariant() const; void checkInvariant() const;
static Mode getMode(const QByteArray &algoName);
QByteArray m_sessionId; QByteArray m_sessionId;
QScopedPointer<Botan::Pipe> m_pipe; QScopedPointer<Botan::Pipe> m_pipe;
@@ -98,8 +101,7 @@ private:
virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const; virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const; virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher, virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
Botan::BlockCipherModePaddingMethod *paddingMethod, Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
virtual char ivChar() const { return 'A'; } virtual char ivChar() const { return 'A'; }
virtual char keyChar() const { return 'C'; } virtual char keyChar() const { return 'C'; }
virtual char macChar() const { return 'E'; } virtual char macChar() const { return 'E'; }
@@ -130,8 +132,7 @@ private:
virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const; virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const; virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher, virtual Botan::Keyed_Filter *makeCipherMode(Botan::BlockCipher *cipher,
Botan::BlockCipherModePaddingMethod *paddingMethod, Mode mode, const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
virtual char ivChar() const { return 'B'; } virtual char ivChar() const { return 'B'; }
virtual char keyChar() const { return 'D'; } virtual char keyChar() const { return 'D'; }
virtual char macChar() const { return 'F'; } virtual char macChar() const { return 'F'; }