Accepting request 705071 from devel:libraries:c_c++

Add support for new AES-GCM ciphers; (bsc#1134193)

OBS-URL: https://build.opensuse.org/request/show/705071
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/libssh?expand=0&rev=56
This commit is contained in:
Dominique Leuenberger 2019-05-25 11:34:36 +00:00 committed by Git OBS Bridge
commit a0e8bc8bc9
5 changed files with 948 additions and 0 deletions

View File

@ -0,0 +1,519 @@
From 46090facbae8c8292d2775546082ccbd5e56fbd1 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Mon, 8 Oct 2018 13:24:49 +0200
Subject: [PATCH] libcrypto: Implement OpenSSH-compatible AES-GCM ciphers using
OpenSSL
The commit also propares the internals throughout the code base
for the inclusion of a new AEAD cipher, because previously, the
source code counted only with chacha20-poly1305 cipher, which
is very specific in many cases.
The SSH_HMAC_AEAD_GCM mac algorithm is not actually used, but the name
needed to be defined so we can match in the algorithms selection per
OpenSSH specification (MACs are ignored in case GCM is select as a cipher [1]).
If the provided OpenSSL does not provide EVP_aes_128_gcm() function,
the AES-GCM ciphers will not be compiled in.
[1] https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.chacha20poly1305?annotate=HEAD
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
ConfigureChecks.cmake | 4 +
config.h.cmake | 3 +
include/libssh/crypto.h | 8 +-
include/libssh/wrapper.h | 3 +-
src/chachapoly.c | 1 +
src/kex.c | 9 +-
src/libcrypto.c | 247 +++++++++++++++++++++++++++++++++++++++
src/packet_crypt.c | 5 +-
src/wrapper.c | 27 ++++-
9 files changed, 297 insertions(+), 10 deletions(-)
Index: libssh-0.8.7/ConfigureChecks.cmake
===================================================================
--- libssh-0.8.7.orig/ConfigureChecks.cmake
+++ libssh-0.8.7/ConfigureChecks.cmake
@@ -110,6 +110,10 @@ if (OPENSSL_FOUND)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ check_function_exists(EVP_aes_128_gcm HAVE_OPENSSL_EVP_AES_GCM)
+
+ set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
check_function_exists(CRYPTO_THREADID_set_callback HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
Index: libssh-0.8.7/config.h.cmake
===================================================================
--- libssh-0.8.7.orig/config.h.cmake
+++ libssh-0.8.7/config.h.cmake
@@ -100,6 +100,9 @@
/* Define to 1 if you have the `EVP_aes128_cbc' function. */
#cmakedefine HAVE_OPENSSL_EVP_AES_CBC 1
+/* Define to 1 if you have the `EVP_aes128_gcm' function. */
+#cmakedefine HAVE_OPENSSL_EVP_AES_GCM 1
+
/* Define to 1 if you have the `CRYPTO_THREADID_set_callback' function. */
#cmakedefine HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK 1
Index: libssh-0.8.7/include/libssh/crypto.h
===================================================================
--- libssh-0.8.7.orig/include/libssh/crypto.h
+++ libssh-0.8.7/include/libssh/crypto.h
@@ -48,6 +48,9 @@
#define DIGEST_MAX_LEN 64
+#define AES_GCM_TAGLEN 16
+#define AES_GCM_IVLEN 12
+
enum ssh_key_exchange_e {
/* diffie-hellman-group1-sha1 */
SSH_KEX_DH_GROUP1_SHA1=1,
@@ -78,7 +81,10 @@ enum ssh_cipher_e {
SSH_AES256_CBC,
SSH_AES128_CTR,
SSH_AES192_CTR,
- SSH_AES256_CTR
+ SSH_AES256_CTR,
+ SSH_AEAD_AES128_GCM,
+ SSH_AEAD_AES256_GCM,
+ SSH_AEAD_CHACHA20_POLY1305
};
struct ssh_crypto_struct {
Index: libssh-0.8.7/include/libssh/wrapper.h
===================================================================
--- libssh-0.8.7.orig/include/libssh/wrapper.h
+++ libssh-0.8.7/include/libssh/wrapper.h
@@ -46,7 +46,8 @@ enum ssh_hmac_e {
SSH_HMAC_SHA256,
SSH_HMAC_SHA512,
SSH_HMAC_MD5,
- SSH_HMAC_AEAD_POLY1305
+ SSH_HMAC_AEAD_POLY1305,
+ SSH_HMAC_AEAD_GCM
};
enum ssh_des_e {
Index: libssh-0.8.7/src/chachapoly.c
===================================================================
--- libssh-0.8.7.orig/src/chachapoly.c
+++ libssh-0.8.7/src/chachapoly.c
@@ -192,6 +192,7 @@ static void chacha20_cleanup(struct ssh_
}
const struct ssh_cipher_struct chacha20poly1305_cipher = {
+ .ciphertype = SSH_AEAD_CHACHA20_POLY1305,
.name = "chacha20-poly1305@openssh.com",
.blocksize = 8,
.lenfield_blocksize = 4,
Index: libssh-0.8.7/src/kex.c
===================================================================
--- libssh-0.8.7.orig/src/kex.c
+++ libssh-0.8.7/src/kex.c
@@ -61,10 +61,15 @@
# endif /* HAVE_OPENSSL_BLOWFISH_H */
# ifdef HAVE_OPENSSL_AES_H
+# ifdef HAVE_OPENSSL_EVP_AES_GCM
+# define GCM "aes256-gcm@openssh.com,aes128-gcm@openssh.com,"
+# else
+# define GCM ""
+# endif /* HAVE_OPENSSL_EVP_AES_GCM */
# ifdef BROKEN_AES_CTR
-# define AES "aes256-cbc,aes192-cbc,aes128-cbc,"
+# define AES GCM "aes256-cbc,aes192-cbc,aes128-cbc,"
# else /* BROKEN_AES_CTR */
-# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
+# define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
# endif /* BROKEN_AES_CTR */
# else /* HAVE_OPENSSL_AES_H */
# define AES ""
Index: libssh-0.8.7/src/libcrypto.c
===================================================================
--- libssh-0.8.7.orig/src/libcrypto.c
+++ libssh-0.8.7/src/libcrypto.c
@@ -491,6 +491,19 @@ static void evp_cipher_init(struct ssh_c
SSH_LOG(SSH_LOG_WARNING, "This cipher is not available in evp_cipher_init");
break;
#endif
+#ifdef HAVE_OPENSSL_EVP_AES_GCM
+ case SSH_AEAD_AES128_GCM:
+ cipher->cipher = EVP_aes_128_gcm();
+ break;
+ case SSH_AEAD_AES256_GCM:
+ cipher->cipher = EVP_aes_256_gcm();
+ break;
+#else
+ case SSH_AEAD_AES128_GCM:
+ case SSH_AEAD_AES256_GCM:
+ SSH_LOG(SSH_LOG_WARNING, "This cipher is not available in evp_cipher_init");
+ break;
+#endif /* HAVE_OPENSSL_EVP_AES_GCM */
case SSH_3DES_CBC:
cipher->cipher = EVP_des_ede3_cbc();
break;
@@ -498,6 +511,9 @@ static void evp_cipher_init(struct ssh_c
cipher->cipher = EVP_bf_cbc();
break;
/* ciphers not using EVP */
+ case SSH_AEAD_CHACHA20_POLY1305:
+ SSH_LOG(SSH_LOG_WARNING, "The ChaCha cipher can not be handled here");
+ break;
case SSH_NO_CIPHER:
SSH_LOG(SSH_LOG_WARNING, "No valid ciphertype found");
break;
@@ -517,6 +533,22 @@ static int evp_cipher_set_encrypt_key(st
SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptInit_ex failed");
return SSH_ERROR;
}
+
+#ifdef HAVE_OPENSSL_EVP_AES_GCM
+ /* For AES-GCM we need to set IV in specific way */
+ if (cipher->ciphertype == SSH_AEAD_AES128_GCM ||
+ cipher->ciphertype == SSH_AEAD_AES256_GCM) {
+ rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
+ EVP_CTRL_GCM_SET_IV_FIXED,
+ -1,
+ (u_char *)IV);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed");
+ return SSH_ERROR;
+ }
+ }
+#endif /* HAVE_OPENSSL_EVP_AES_GCM */
+
EVP_CIPHER_CTX_set_padding(cipher->ctx, 0);
return SSH_OK;
@@ -534,6 +566,22 @@ static int evp_cipher_set_decrypt_key(st
SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptInit_ex failed");
return SSH_ERROR;
}
+
+#ifdef HAVE_OPENSSL_EVP_AES_GCM
+ /* For AES-GCM we need to set IV in specific way */
+ if (cipher->ciphertype == SSH_AEAD_AES128_GCM ||
+ cipher->ciphertype == SSH_AEAD_AES256_GCM) {
+ rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
+ EVP_CTRL_GCM_SET_IV_FIXED,
+ -1,
+ (u_char *)IV);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed");
+ return SSH_ERROR;
+ }
+ }
+#endif /* HAVE_OPENSSL_EVP_AES_GCM */
+
EVP_CIPHER_CTX_set_padding(cipher->ctx, 0);
return SSH_OK;
@@ -642,6 +690,175 @@ static void aes_ctr_cleanup(struct ssh_c
#endif /* HAVE_OPENSSL_EVP_AES_CTR */
+#ifdef HAVE_OPENSSL_EVP_AES_GCM
+static int
+evp_cipher_aead_get_length(struct ssh_cipher_struct *cipher,
+ void *in,
+ uint8_t *out,
+ size_t len,
+ uint64_t seq)
+{
+ (void)seq;
+
+ /* The length is not encrypted: Copy it to the result buffer */
+ memcpy(out, in, len);
+
+ return SSH_OK;
+}
+
+static void
+evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
+ void *in,
+ void *out,
+ size_t len,
+ uint8_t *tag,
+ uint64_t seq)
+{
+ size_t authlen, aadlen;
+ u_char lastiv[1];
+ int outlen = 0;
+ int rc;
+
+ (void) seq;
+
+ aadlen = cipher->lenfield_blocksize;
+ authlen = cipher->tag_size;
+
+ /* increment IV */
+ rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
+ EVP_CTRL_GCM_IV_GEN,
+ 1,
+ lastiv);
+ if (rc == 0) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed");
+ return;
+ }
+
+ /* Pass over the authenticated data (not encrypted) */
+ rc = EVP_EncryptUpdate(cipher->ctx,
+ NULL,
+ &outlen,
+ (unsigned char *)in,
+ aadlen);
+ if (rc == 0 || outlen != aadlen) {
+ SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data");
+ return;
+ }
+ memcpy(out, in, aadlen);
+
+ /* Encrypt the rest of the data */
+ rc = EVP_EncryptUpdate(cipher->ctx,
+ (unsigned char *)out + aadlen,
+ &outlen,
+ (unsigned char *)in + aadlen,
+ len - aadlen);
+ if (rc != 1 || outlen != len - aadlen) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed");
+ return;
+ }
+
+ /* compute tag */
+ rc = EVP_EncryptFinal(cipher->ctx,
+ NULL,
+ &outlen);
+ if (rc < 0) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptFinal failed: Failed to create a tag");
+ return;
+ }
+
+ rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
+ EVP_CTRL_GCM_GET_TAG,
+ authlen,
+ (unsigned char *)tag);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_GET_TAG failed");
+ return;
+ }
+}
+
+static int
+evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
+ void *complete_packet,
+ uint8_t *out,
+ size_t encrypted_size,
+ uint64_t seq)
+{
+ size_t authlen, aadlen;
+ u_char lastiv[1];
+ int outlen = 0;
+ int rc = 0;
+
+ (void)seq;
+
+ aadlen = cipher->lenfield_blocksize;
+ authlen = cipher->tag_size;
+
+ /* increment IV */
+ rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
+ EVP_CTRL_GCM_IV_GEN,
+ 1,
+ lastiv);
+ if (rc == 0) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed");
+ return SSH_ERROR;
+ }
+
+ /* set tag for authentication */
+ rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
+ EVP_CTRL_GCM_SET_TAG,
+ authlen,
+ (unsigned char *)complete_packet + aadlen + encrypted_size);
+ if (rc == 0) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_TAG failed");
+ return SSH_ERROR;
+ }
+
+ /* Pass over the authenticated data (not encrypted) */
+ rc = EVP_DecryptUpdate(cipher->ctx,
+ NULL,
+ &outlen,
+ (unsigned char *)complete_packet,
+ aadlen);
+ if (rc == 0) {
+ SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data");
+ return SSH_ERROR;
+ }
+ /* Do not copy the length to the target buffer, because it is already processed */
+ //memcpy(out, complete_packet, aadlen);
+
+ /* Decrypt the rest of the data */
+ rc = EVP_DecryptUpdate(cipher->ctx,
+ (unsigned char *)out,
+ &outlen,
+ (unsigned char *)complete_packet + aadlen,
+ encrypted_size /* already substracted aadlen*/);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptUpdate failed");
+ return SSH_ERROR;
+ }
+
+ if (outlen != (int)encrypted_size) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "EVP_DecryptUpdate: output size %d for %zd in",
+ outlen,
+ encrypted_size);
+ return SSH_ERROR;
+ }
+
+ /* verify tag */
+ rc = EVP_DecryptFinal(cipher->ctx,
+ NULL,
+ &outlen);
+ if (rc < 0) {
+ SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptFinal failed: Failed authentication");
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+#endif /* HAVE_OPENSSL_EVP_AES_GCM */
+
/*
* The table of supported ciphers
*/
@@ -765,6 +982,36 @@ static struct ssh_cipher_struct ssh_ciph
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
},
+#ifdef HAVE_OPENSSL_EVP_AES_GCM
+ {
+ .name = "aes128-gcm@openssh.com",
+ .blocksize = AES_BLOCK_SIZE,
+ .lenfield_blocksize = 4, /* not encrypted, but authenticated */
+ .ciphertype = SSH_AEAD_AES128_GCM,
+ .keysize = 128,
+ .tag_size = AES_GCM_TAGLEN,
+ .set_encrypt_key = evp_cipher_set_encrypt_key,
+ .set_decrypt_key = evp_cipher_set_decrypt_key,
+ .aead_encrypt = evp_cipher_aead_encrypt,
+ .aead_decrypt_length = evp_cipher_aead_get_length,
+ .aead_decrypt = evp_cipher_aead_decrypt,
+ .cleanup = evp_cipher_cleanup
+ },
+ {
+ .name = "aes256-gcm@openssh.com",
+ .blocksize = AES_BLOCK_SIZE,
+ .lenfield_blocksize = 4, /* not encrypted, but authenticated */
+ .ciphertype = SSH_AEAD_AES256_GCM,
+ .keysize = 256,
+ .tag_size = AES_GCM_TAGLEN,
+ .set_encrypt_key = evp_cipher_set_encrypt_key,
+ .set_decrypt_key = evp_cipher_set_decrypt_key,
+ .aead_encrypt = evp_cipher_aead_encrypt,
+ .aead_decrypt_length = evp_cipher_aead_get_length,
+ .aead_decrypt = evp_cipher_aead_decrypt,
+ .cleanup = evp_cipher_cleanup
+ },
+#endif /* HAVE_OPENSSL_EVP_AES_GCM */
#endif /* HAS_AES */
#ifdef HAS_DES
{
Index: libssh-0.8.7/src/packet_crypt.c
===================================================================
--- libssh-0.8.7.orig/src/packet_crypt.c
+++ libssh-0.8.7/src/packet_crypt.c
@@ -209,8 +209,9 @@ int ssh_packet_hmac_verify(ssh_session s
unsigned int len;
uint32_t seq;
- /* AEAD type have no mac checking */
- if (type == SSH_HMAC_AEAD_POLY1305) {
+ /* AEAD types have no mac checking */
+ if (type == SSH_HMAC_AEAD_POLY1305 ||
+ type == SSH_HMAC_AEAD_GCM) {
return SSH_OK;
}
Index: libssh-0.8.7/src/wrapper.c
===================================================================
--- libssh-0.8.7.orig/src/wrapper.c
+++ libssh-0.8.7/src/wrapper.c
@@ -55,6 +55,7 @@ static struct ssh_hmac_struct ssh_hmac_t
{ "hmac-sha2-512", SSH_HMAC_SHA512 },
{ "hmac-md5", SSH_HMAC_MD5 },
{ "aead-poly1305", SSH_HMAC_AEAD_POLY1305 },
+ { "aead-gcm", SSH_HMAC_AEAD_GCM },
{ NULL, 0}
};
@@ -74,6 +75,8 @@ size_t hmac_digest_len(enum ssh_hmac_e t
return MD5_DIGEST_LEN;
case SSH_HMAC_AEAD_POLY1305:
return POLY1305_TAGLEN;
+ case SSH_HMAC_AEAD_GCM:
+ return AES_GCM_TAGLEN;
default:
return 0;
}
@@ -253,7 +256,11 @@ static int crypt_set_algorithms2(ssh_ses
if (session->next_crypto->out_cipher->aead_encrypt != NULL){
/* this cipher has integrated MAC */
- wanted = "aead-poly1305";
+ if (session->next_crypto->out_cipher->ciphertype == SSH_AEAD_CHACHA20_POLY1305) {
+ wanted = "aead-poly1305";
+ } else {
+ wanted = "aead-gcm";
+ }
} else {
/*
* We must scan the kex entries to find hmac algorithms and set their
@@ -307,7 +314,11 @@ static int crypt_set_algorithms2(ssh_ses
if (session->next_crypto->in_cipher->aead_encrypt != NULL){
/* this cipher has integrated MAC */
- wanted = "aead-poly1305";
+ if (session->next_crypto->in_cipher->ciphertype == SSH_AEAD_CHACHA20_POLY1305) {
+ wanted = "aead-poly1305";
+ } else {
+ wanted = "aead-gcm";
+ }
} else {
/* we must scan the kex entries to find hmac algorithms and set their appropriate structure */
wanted = session->next_crypto->kex_methods[SSH_MAC_S_C];
@@ -395,7 +406,11 @@ int crypt_set_algorithms_server(ssh_sess
i=0;
if (session->next_crypto->out_cipher->aead_encrypt != NULL){
/* this cipher has integrated MAC */
- method = "aead-poly1305";
+ if (session->next_crypto->out_cipher->ciphertype == SSH_AEAD_CHACHA20_POLY1305) {
+ method = "aead-poly1305";
+ } else {
+ method = "aead-gcm";
+ }
} else {
/* we must scan the kex entries to find hmac algorithms and set their appropriate structure */
/* out */
@@ -446,7 +461,11 @@ int crypt_set_algorithms_server(ssh_sess
if (session->next_crypto->in_cipher->aead_encrypt != NULL){
/* this cipher has integrated MAC */
- method = "aead-poly1305";
+ if (session->next_crypto->in_cipher->ciphertype == SSH_AEAD_CHACHA20_POLY1305) {
+ method = "aead-poly1305";
+ } else {
+ method = "aead-gcm";
+ }
} else {
/* we must scan the kex entries to find hmac algorithms and set their appropriate structure */
method = session->next_crypto->kex_methods[SSH_MAC_C_S];

View File

@ -0,0 +1,277 @@
From 5790036a2305d5610ac55adb5382ea55d043998f Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Mon, 1 Oct 2018 14:32:05 +0200
Subject: [PATCH] libgcrypt: Implement OpenSSH-compatible AES-GCM ciphers using
libgcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
include/libssh/crypto.h | 1 +
src/kex.c | 4 +-
src/libgcrypt.c | 201 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 205 insertions(+), 1 deletion(-)
diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h
index fc375a4f..8777f0c9 100644
--- a/include/libssh/crypto.h
+++ b/include/libssh/crypto.h
@@ -142,6 +142,7 @@ struct ssh_cipher_struct {
size_t keylen; /* length of the key structure */
#ifdef HAVE_LIBGCRYPT
gcry_cipher_hd_t *key;
+ unsigned char last_iv[AES_GCM_IVLEN];
#elif defined HAVE_LIBCRYPTO
struct ssh_3des_key_schedule *des3_key;
struct ssh_aes_key_schedule *aes_key;
diff --git a/src/kex.c b/src/kex.c
index e0fd5680..382d88fb 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -40,7 +40,9 @@
#ifdef HAVE_LIBGCRYPT
# define BLOWFISH "blowfish-cbc,"
-# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
+# define AES "aes256-gcm@openssh.com,aes128-gcm@openssh.com," \
+ "aes256-ctr,aes192-ctr,aes128-ctr," \
+ "aes256-cbc,aes192-cbc,aes128-cbc,"
# define DES "3des-cbc"
# define DES_SUPPORTED "3des-cbc"
diff --git a/src/libgcrypt.c b/src/libgcrypt.c
index f004ffe2..7160bb1c 100644
--- a/src/libgcrypt.c
+++ b/src/libgcrypt.c
@@ -353,6 +353,8 @@ static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
}
if(strstr(cipher->name,"-ctr"))
mode=GCRY_CIPHER_MODE_CTR;
+ if (strstr(cipher->name, "-gcm"))
+ mode = GCRY_CIPHER_MODE_GCM;
switch (cipher->keysize) {
case 128:
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES128,
@@ -386,6 +388,11 @@ static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
SAFE_FREE(cipher->key);
return -1;
}
+ } else if (mode == GCRY_CIPHER_MODE_GCM) {
+ /* Store the IV so we can handle the packet counter increments later
+ * The IV is passed to the cipher context later.
+ */
+ memcpy(cipher->last_iv, IV, AES_GCM_IVLEN);
} else {
if(gcry_cipher_setctr(cipher->key[0],IV,16)){
SAFE_FREE(cipher->key);
@@ -407,6 +414,172 @@ static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
+static int
+aes_aead_get_length(struct ssh_cipher_struct *cipher,
+ void *in,
+ uint8_t *out,
+ size_t len,
+ uint64_t seq)
+{
+ (void)seq;
+
+ /* The length is not encrypted: Copy it to the result buffer */
+ memcpy(out, in, len);
+
+ return SSH_OK;
+}
+
+/* Increment 64b integer in network byte order */
+static void
+uint64_inc(unsigned char *counter)
+{
+ int i;
+
+ for (i = 7; i >= 0; i--) {
+ counter[i]++;
+ if (counter[i])
+ return;
+ }
+}
+
+static void
+aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
+ void *in,
+ void *out,
+ size_t len,
+ uint8_t *tag,
+ uint64_t seq)
+{
+ gpg_error_t err;
+ size_t aadlen, authlen;
+
+ (void)seq;
+
+ aadlen = cipher->lenfield_blocksize;
+ authlen = cipher->tag_size;
+
+ /* increment IV */
+ err = gcry_cipher_setiv(cipher->key[0],
+ cipher->last_iv,
+ AES_GCM_IVLEN);
+ /* This actualy does not increment the packet counter for the
+ * current encryption operation, but for the next one. The first
+ * operation needs to be completed with the derived IV.
+ *
+ * The IV buffer has the following structure:
+ * [ 4B static IV ][ 8B packet counter ][ 4B block counter ]
+ */
+ uint64_inc(cipher->last_iv + 4);
+ if (err) {
+ SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ gpg_strerror(err));
+ return;
+ }
+
+ /* Pass the authenticated data (packet_length) */
+ err = gcry_cipher_authenticate(cipher->key[0], in, aadlen);
+ if (err) {
+ SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_authenticate failed: %s",
+ gpg_strerror(err));
+ return;
+ }
+ memcpy(out, in, aadlen);
+
+ /* Encrypt the rest of the data */
+ err = gcry_cipher_encrypt(cipher->key[0],
+ (unsigned char *)out + aadlen,
+ len - aadlen,
+ (unsigned char *)in + aadlen,
+ len - aadlen);
+ if (err) {
+ SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ gpg_strerror(err));
+ return;
+ }
+
+ /* Calculate the tag */
+ err = gcry_cipher_gettag(cipher->key[0],
+ (void *)tag,
+ authlen);
+ if (err) {
+ SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_gettag failed: %s",
+ gpg_strerror(err));
+ return;
+ }
+}
+
+static int
+aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
+ void *complete_packet,
+ uint8_t *out,
+ size_t encrypted_size,
+ uint64_t seq)
+{
+ gpg_error_t err;
+ size_t aadlen, authlen;
+
+ (void)seq;
+
+ aadlen = cipher->lenfield_blocksize;
+ authlen = cipher->tag_size;
+
+ /* increment IV */
+ err = gcry_cipher_setiv(cipher->key[0],
+ cipher->last_iv,
+ AES_GCM_IVLEN);
+ /* This actualy does not increment the packet counter for the
+ * current encryption operation, but for the next one. The first
+ * operation needs to be completed with the derived IV.
+ *
+ * The IV buffer has the following structure:
+ * [ 4B static IV ][ 8B packet counter ][ 4B block counter ]
+ */
+ uint64_inc(cipher->last_iv + 4);
+ if (err) {
+ SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ gpg_strerror(err));
+ return SSH_ERROR;
+ }
+
+ /* Pass the authenticated data (packet_length) */
+ err = gcry_cipher_authenticate(cipher->key[0],
+ complete_packet,
+ aadlen);
+ if (err) {
+ SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_authenticate failed: %s",
+ gpg_strerror(err));
+ return SSH_ERROR;
+ }
+ /* Do not copy the length to the target buffer, because it is already processed */
+ //memcpy(out, complete_packet, aadlen);
+
+ /* Encrypt the rest of the data */
+ err = gcry_cipher_decrypt(cipher->key[0],
+ out,
+ encrypted_size,
+ (unsigned char *)complete_packet + aadlen,
+ encrypted_size);
+ if (err) {
+ SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_decrypt failed: %s",
+ gpg_strerror(err));
+ return SSH_ERROR;
+ }
+
+ /* Check the tag */
+ err = gcry_cipher_checktag(cipher->key[0],
+ (unsigned char *)complete_packet + aadlen + encrypted_size,
+ authlen);
+ if (gpg_err_code(err) == GPG_ERR_CHECKSUM) {
+ SSH_LOG(SSH_LOG_WARNING, "The authentication tag does not match");
+ return SSH_ERROR;
+ } else if (err != GPG_ERR_NO_ERROR) {
+ SSH_LOG(SSH_LOG_WARNING, "General error while decryption: %s",
+ gpg_strerror(err));
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
@@ -519,6 +692,34 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.encrypt = aes_encrypt,
.decrypt = aes_decrypt
},
+ {
+ .name = "aes128-gcm@openssh.com",
+ .blocksize = 16,
+ .lenfield_blocksize = 4, /* not encrypted, but authenticated */
+ .keylen = sizeof(gcry_cipher_hd_t),
+ .key = NULL,
+ .keysize = 128,
+ .tag_size = AES_GCM_TAGLEN,
+ .set_encrypt_key = aes_set_key,
+ .set_decrypt_key = aes_set_key,
+ .aead_encrypt = aes_gcm_encrypt,
+ .aead_decrypt_length = aes_aead_get_length,
+ .aead_decrypt = aes_gcm_decrypt,
+ },
+ {
+ .name = "aes256-gcm@openssh.com",
+ .blocksize = 16,
+ .lenfield_blocksize = 4, /* not encrypted, but authenticated */
+ .keylen = sizeof(gcry_cipher_hd_t),
+ .key = NULL,
+ .keysize = 256,
+ .tag_size = AES_GCM_TAGLEN,
+ .set_encrypt_key = aes_set_key,
+ .set_decrypt_key = aes_set_key,
+ .aead_encrypt = aes_gcm_encrypt,
+ .aead_decrypt_length = aes_aead_get_length,
+ .aead_decrypt = aes_gcm_decrypt,
+ },
{
.name = "3des-cbc",
.blocksize = 8,
--
2.21.0

View File

@ -0,0 +1,141 @@
From 42bd7cdf6c6e50786155c904821fc62d6d8e5acb Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Mon, 8 Oct 2018 13:31:12 +0200
Subject: [PATCH] tests: Add aes-gcm ciphers tests
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
tests/client/torture_algorithms.c | 20 ++++++++++++++++++++
tests/pkd/pkd_hello.c | 29 +++++++++++++++++++++++++++--
2 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/tests/client/torture_algorithms.c b/tests/client/torture_algorithms.c
index c216c99b..9bb67410 100644
--- a/tests/client/torture_algorithms.c
+++ b/tests/client/torture_algorithms.c
@@ -240,6 +240,20 @@ static void torture_algorithms_aes256_ctr_hmac_sha2_512(void **state) {
test_algorithm(s->ssh.session, NULL/*kex*/, "aes256-ctr", "hmac-sha2-512");
}
+static void torture_algorithms_aes128_gcm(void **state)
+{
+ struct torture_state *s = *state;
+
+ test_algorithm(s->ssh.session, NULL/*kex*/, "aes128-gcm@openssh.com", NULL);
+}
+
+static void torture_algorithms_aes256_gcm(void **state)
+{
+ struct torture_state *s = *state;
+
+ test_algorithm(s->ssh.session, NULL/*kex*/, "aes256-gcm@openssh.com", NULL);
+}
+
static void torture_algorithms_3des_cbc_hmac_sha1(void **state) {
struct torture_state *s = *state;
@@ -464,6 +478,12 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_algorithms_aes256_ctr_hmac_sha2_512,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_algorithms_aes128_gcm,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_algorithms_aes256_gcm,
+ session_setup,
+ session_teardown),
cmocka_unit_test_setup_teardown(torture_algorithms_3des_cbc_hmac_sha1,
session_setup,
session_teardown),
diff --git a/tests/pkd/pkd_hello.c b/tests/pkd/pkd_hello.c
index e14316ec..0fa72d04 100644
--- a/tests/pkd/pkd_hello.c
+++ b/tests/pkd/pkd_hello.c
@@ -383,6 +383,8 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
#endif
#define CHACHA20 "chacha20-poly1305@openssh.com"
+#define AES128_GCM "aes128-gcm@openssh.com"
+#define AES256_GCM "aes256-gcm@openssh.com"
#ifdef HAVE_DSA
#define PKDTESTS_CIPHER_OPENSSHONLY(f, client, ciphercmd) \
@@ -390,9 +392,13 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \
f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \
f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown) \
+ f(client, rsa_aes128_gcm, ciphercmd(AES128_GCM), setup_rsa, teardown) \
+ f(client, rsa_aes256_gcm, ciphercmd(AES256_GCM), setup_rsa, teardown) \
f(client, dsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_dsa, teardown) \
f(client, dsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_dsa, teardown) \
f(client, dsa_chacha20, ciphercmd(CHACHA20), setup_dsa, teardown) \
+ f(client, dsa_aes128_gcm, ciphercmd(AES128_GCM), setup_dsa, teardown) \
+ f(client, dsa_aes256_gcm, ciphercmd(AES256_GCM), setup_dsa, teardown) \
f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown) \
@@ -401,21 +407,31 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown) \
f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown) \
+ f(client, ed25519_aes128_gcm, ciphercmd(AES128_GCM), setup_ed25519, teardown) \
+ f(client, ed25519_aes256_gcm, ciphercmd(AES256_GCM), setup_ed25519, teardown) \
f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown)
+ f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_521, teardown)
#else
#define PKDTESTS_CIPHER_OPENSSHONLY(f, client, ciphercmd) \
/* Ciphers. */ \
f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \
f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \
f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown) \
+ f(client, rsa_aes128_gcm, ciphercmd(AES128_GCM), setup_rsa, teardown) \
+ f(client, rsa_aes256_gcm, ciphercmd(AES256_GCM), setup_rsa, teardown) \
f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown) \
@@ -424,17 +440,26 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown) \
f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown) \
+ f(client, ed25519_aes128_gcm, ciphercmd(AES128_GCM), setup_ed25519, teardown) \
+ f(client, ed25519_aes256_gcm, ciphercmd(AES256_GCM), setup_ed25519, teardown) \
f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown)
+ f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_521, teardown)
#endif
+
#ifdef HAVE_DSA
#define PKDTESTS_MAC(f, client, maccmd) \
/* MACs. */ \
--
2.21.0

View File

@ -1,3 +1,11 @@
-------------------------------------------------------------------
Thu May 23 15:32:07 UTC 2019 - James McDonough <jmcdonough@suse.com>
- Add support for new AES-GCM encryption types; (bsc#1134193)
* Add 0001-libcrypto-Implement-OpenSSH-compatible-AES-GCM-ciphe.patch
* Add 0001-libgcrypt-Implement-OpenSSH-compatible-AES-GCM-ciphe.patch
* Add 0001-tests-Add-aes-gcm-ciphers-tests.patch
-------------------------------------------------------------------
Thu Feb 28 07:17:52 UTC 2019 - Stephan Kulow <coolo@suse.com>

View File

@ -36,6 +36,9 @@ Source1: https://www.libssh.org/files/0.8/libssh-%{version}.tar.xz.asc
Source2: https://cryptomilk.org/gpgkey-8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D.gpg#/libssh.keyring
Source99: baselibs.conf
Patch0: 0001-disable-timeout-test-on-slow-buildsystems.patch
Patch1: 0001-libcrypto-Implement-OpenSSH-compatible-AES-GCM-ciphe.patch
Patch2: 0001-libgcrypt-Implement-OpenSSH-compatible-AES-GCM-ciphe.patch
Patch3: 0001-tests-Add-aes-gcm-ciphers-tests.patch
# cmake pulls curl, but libssh is a dependency of it, so avoid cycles by using curl-mini
#!BuildRequires: libcurl4-mini
BuildRequires: cmake