Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.2:Staging:N
libssh
0001-libgcrypt-Implement-OpenSSH-compatible-AES...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0001-libgcrypt-Implement-OpenSSH-compatible-AES-GCM-ciphe.patch of Package libssh
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
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