aboutsummaryrefslogtreecommitdiff
path: root/src/libmbedcrypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmbedcrypto.c')
-rw-r--r--src/libmbedcrypto.c159
1 files changed, 159 insertions, 0 deletions
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 <mbedtls/md.h>
+#ifdef MBEDTLS_GCM_C
+#include <mbedtls/gcm.h>
+#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,