1
1

libgcrypt: Implement OpenSSH-compatible AES-GCM ciphers using libgcrypt

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
Jakub Jelen 2018-10-01 14:32:05 +02:00 коммит произвёл Andreas Schneider
родитель 032f486f27
Коммит 5790036a23
3 изменённых файлов: 205 добавлений и 1 удалений

Просмотреть файл

@ -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;

Просмотреть файл

@ -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"

Просмотреть файл

@ -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,