From 72bd2fe1972673ca173cba635472d6e7ad5783d1 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Thu, 4 Oct 2018 15:21:36 +0200 Subject: libmbedtls: Support OpenSSH-compatible AES-GCM ciphers using mbedTLS Signed-off-by: Jakub Jelen Reviewed-by: Andreas Schneider --- src/libmbedcrypto.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) (limited to 'src/libmbedcrypto.c') diff --git a/src/libmbedcrypto.c b/src/libmbedcrypto.c index 8a2a7d96..8c0f5b4c 100644 --- a/src/libmbedcrypto.c +++ b/src/libmbedcrypto.c @@ -26,9 +26,13 @@ #include "libssh/wrapper.h" #include "libssh/crypto.h" #include "libssh/priv.h" +#include "libssh/misc.h" #ifdef HAVE_LIBMBEDCRYPTO #include +#ifdef MBEDTLS_GCM_C +#include +#endif /* MBEDTLS_GCM_C */ static mbedtls_entropy_context ssh_mbedtls_entropy; static mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg; @@ -616,6 +620,37 @@ error: return SSH_ERROR; } +#ifdef MBEDTLS_GCM_C +static int +cipher_set_key_gcm(struct ssh_cipher_struct *cipher, + void *key, + void *IV) +{ + const mbedtls_cipher_info_t *cipher_info = NULL; + int rc; + + mbedtls_gcm_init(&cipher->gcm_ctx); + cipher_info = mbedtls_cipher_info_from_type(cipher->type); + + rc = mbedtls_gcm_setkey(&cipher->gcm_ctx, + MBEDTLS_CIPHER_ID_AES, + key, + cipher_info->key_bitlen); + if (rc != 0) { + SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_setkey failed"); + goto error; + } + + /* Store the IV so we can increment the packet counter later */ + memcpy(cipher->last_iv, IV, AES_GCM_IVLEN); + + return 0; +error: + mbedtls_gcm_free(&cipher->gcm_ctx); + return 1; +} +#endif /* MBEDTLS_GCM_C */ + static int cipher_set_decrypt_key(struct ssh_cipher_struct *cipher, void *key, @@ -810,10 +845,104 @@ static void cipher_decrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void } +#ifdef MBEDTLS_GCM_C +static int +cipher_gcm_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 +cipher_encrypt_gcm(struct ssh_cipher_struct *cipher, + void *in, + void *out, + size_t len, + uint8_t *tag, + uint64_t seq) +{ + size_t authlen, aadlen; + int rc; + + (void) seq; + + aadlen = cipher->lenfield_blocksize; + authlen = cipher->tag_size; + + /* The length is not encrypted */ + memcpy(out, in, aadlen); + rc = mbedtls_gcm_crypt_and_tag(&cipher->gcm_ctx, + MBEDTLS_GCM_ENCRYPT, + len - aadlen, /* encrypted data len */ + cipher->last_iv, /* IV */ + AES_GCM_IVLEN, + in, /* aad */ + aadlen, + (const unsigned char *)in + aadlen, /* input */ + (unsigned char *)out + aadlen, /* output */ + authlen, + tag); /* tag */ + if (rc != 0) { + SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_crypt_and_tag failed"); + return; + } + + /* Increment the IV for the next invocation */ + uint64_inc(cipher->last_iv + 4); +} + +static int +cipher_decrypt_gcm(struct ssh_cipher_struct *cipher, + void *complete_packet, + uint8_t *out, + size_t encrypted_size, + uint64_t seq) +{ + size_t authlen, aadlen; + int rc; + + (void) seq; + + aadlen = cipher->lenfield_blocksize; + authlen = cipher->tag_size; + + rc = mbedtls_gcm_auth_decrypt(&cipher->gcm_ctx, + encrypted_size, /* encrypted data len */ + cipher->last_iv, /* IV */ + AES_GCM_IVLEN, + complete_packet, /* aad */ + aadlen, + (const u_char *)complete_packet + aadlen + encrypted_size, /* tag */ + authlen, + (const u_char *)complete_packet + aadlen, /* input */ + (unsigned char *)out); /* output */ + if (rc != 0) { + SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_auth_decrypt failed"); + return SSH_ERROR; + } + + /* Increment the IV for the next invocation */ + uint64_inc(cipher->last_iv + 4); + + return SSH_OK; +} +#endif /* MBEDTLS_GCM_C */ + static void cipher_cleanup(struct ssh_cipher_struct *cipher) { mbedtls_cipher_free(&cipher->encrypt_ctx); mbedtls_cipher_free(&cipher->decrypt_ctx); +#ifdef MBEDTLS_GCM_C + mbedtls_gcm_free(&cipher->gcm_ctx); +#endif /* MBEDTLS_GCM_C */ } static struct ssh_cipher_struct ssh_ciphertab[] = { @@ -894,6 +1023,36 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .decrypt = cipher_decrypt_cbc, .cleanup = cipher_cleanup }, +#ifdef MBEDTLS_GCM_C + { + .name = "aes128-gcm@openssh.com", + .blocksize = 16, + .lenfield_blocksize = 4, /* not encrypted, but authenticated */ + .keysize = 128, + .tag_size = AES_GCM_TAGLEN, + .type = MBEDTLS_CIPHER_AES_128_GCM, + .set_encrypt_key = cipher_set_key_gcm, + .set_decrypt_key = cipher_set_key_gcm, + .aead_encrypt = cipher_encrypt_gcm, + .aead_decrypt_length = cipher_gcm_get_length, + .aead_decrypt = cipher_decrypt_gcm, + .cleanup = cipher_cleanup + }, + { + .name = "aes256-gcm@openssh.com", + .blocksize = 16, + .lenfield_blocksize = 4, /* not encrypted, but authenticated */ + .keysize = 256, + .tag_size = AES_GCM_TAGLEN, + .type = MBEDTLS_CIPHER_AES_256_GCM, + .set_encrypt_key = cipher_set_key_gcm, + .set_decrypt_key = cipher_set_key_gcm, + .aead_encrypt = cipher_encrypt_gcm, + .aead_decrypt_length = cipher_gcm_get_length, + .aead_decrypt = cipher_decrypt_gcm, + .cleanup = cipher_cleanup + }, +#endif /* MBEDTLS_GCM_C */ { .name = "3des-cbc", .blocksize = 8, -- cgit v1.2.3