mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-12-15 18:32:26 +03:00
libgcrypt: Implement OpenSSH-compatible AES-GCM ciphers using libgcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
This commit is contained in:
committed by
Andreas Schneider
parent
032f486f27
commit
5790036a23
@@ -142,6 +142,7 @@ struct ssh_cipher_struct {
|
|||||||
size_t keylen; /* length of the key structure */
|
size_t keylen; /* length of the key structure */
|
||||||
#ifdef HAVE_LIBGCRYPT
|
#ifdef HAVE_LIBGCRYPT
|
||||||
gcry_cipher_hd_t *key;
|
gcry_cipher_hd_t *key;
|
||||||
|
unsigned char last_iv[AES_GCM_IVLEN];
|
||||||
#elif defined HAVE_LIBCRYPTO
|
#elif defined HAVE_LIBCRYPTO
|
||||||
struct ssh_3des_key_schedule *des3_key;
|
struct ssh_3des_key_schedule *des3_key;
|
||||||
struct ssh_aes_key_schedule *aes_key;
|
struct ssh_aes_key_schedule *aes_key;
|
||||||
|
|||||||
@@ -40,7 +40,9 @@
|
|||||||
|
|
||||||
#ifdef HAVE_LIBGCRYPT
|
#ifdef HAVE_LIBGCRYPT
|
||||||
# define BLOWFISH "blowfish-cbc,"
|
# 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 "3des-cbc"
|
||||||
# define DES_SUPPORTED "3des-cbc"
|
# define DES_SUPPORTED "3des-cbc"
|
||||||
|
|
||||||
|
|||||||
201
src/libgcrypt.c
201
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"))
|
if(strstr(cipher->name,"-ctr"))
|
||||||
mode=GCRY_CIPHER_MODE_CTR;
|
mode=GCRY_CIPHER_MODE_CTR;
|
||||||
|
if (strstr(cipher->name, "-gcm"))
|
||||||
|
mode = GCRY_CIPHER_MODE_GCM;
|
||||||
switch (cipher->keysize) {
|
switch (cipher->keysize) {
|
||||||
case 128:
|
case 128:
|
||||||
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES128,
|
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);
|
SAFE_FREE(cipher->key);
|
||||||
return -1;
|
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 {
|
} else {
|
||||||
if(gcry_cipher_setctr(cipher->key[0],IV,16)){
|
if(gcry_cipher_setctr(cipher->key[0],IV,16)){
|
||||||
SAFE_FREE(cipher->key);
|
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);
|
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) {
|
static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
|
||||||
if (cipher->key == NULL) {
|
if (cipher->key == NULL) {
|
||||||
if (alloc_key(cipher) < 0) {
|
if (alloc_key(cipher) < 0) {
|
||||||
@@ -519,6 +692,34 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
|
|||||||
.encrypt = aes_encrypt,
|
.encrypt = aes_encrypt,
|
||||||
.decrypt = aes_decrypt
|
.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",
|
.name = "3des-cbc",
|
||||||
.blocksize = 8,
|
.blocksize = 8,
|
||||||
|
|||||||
Reference in New Issue
Block a user