Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.2:Staging:N
libssh
0001-libcrypto-Implement-OpenSSH-compatible-AES...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0001-libcrypto-Implement-OpenSSH-compatible-AES-GCM-ciphe.patch of Package libssh
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];
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor