Commit 45de9ee2 authored by Christian Kandeler's avatar Christian Kandeler

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: default avatarJoerg Bornemann <joerg.bornemann@theqtcompany.com>
Reviewed-by: default avatarChristian Kandeler <christian.kandeler@digia.com>
parent c0d6c7c5
......@@ -32,6 +32,7 @@
#define SSHBOTANCONVERSIONS_P_H
#include "sshcapabilities_p.h"
#include "sshexception_p.h"
#include <botan/botan.h>
......@@ -63,10 +64,22 @@ inline const char *botanKeyExchangeAlgoName(const QByteArray &rfcAlgoName)
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)
......
......@@ -62,11 +62,19 @@ const QList<QByteArray> SshCapabilities::PublicKeyAlgorithms
= 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");
......
......@@ -48,8 +48,12 @@ public:
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;
......
......@@ -65,6 +65,16 @@ void SshAbstractCryptoFacility::clearKeys()
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();
......@@ -72,8 +82,9 @@ void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
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);
......@@ -82,8 +93,8 @@ void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
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));
......@@ -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,
quint32 dataSize) const
{
......@@ -168,11 +188,16 @@ QByteArray SshEncryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
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
......@@ -360,11 +385,16 @@ QByteArray SshDecryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
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,
......
......@@ -53,9 +53,13 @@ public:
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 &);
......@@ -64,15 +68,14 @@ private:
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;
......@@ -98,8 +101,7 @@ private:
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'; }
......@@ -130,8 +132,7 @@ private:
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'; }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment