libgcrypt: Implement OpenSSH-compatible AES-GCM ciphers using libgcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
родитель
032f486f27
Коммит
5790036a23
@ -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"
|
||||
|
||||
|
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"))
|
||||
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,
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user