Subject: [PATCH] src/crypt.c: Fallback to implement AES CTR mode using CBC mode

[PATCH] src/crypt.c: Fallback to implement AES CTR mode using CBC mode

From: Peter Stuge <peter_at_stuge.se>
Date: Sun, 15 Apr 2018 19:37:29 +0200

If the crypto backend supports AES CTR mode directly then nothing changes.

Otherwise, libssh2 supports AES CTR ciphers anyway, as long as the crypto
backend implements AES CBC mode, at the cost of some overhead; the CBC
algorithm must be re-initialized every $blocksize bytes.

This CTR implementation is algorithm independent, but is currently used
only for AES ciphers.

---
 src/crypt.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 167 insertions(+), 15 deletions(-)
diff --git a/src/crypt.c b/src/crypt.c
index 9d73c23..8675bcc 100644
--- a/src/crypt.c
+++ b/src/crypt.c
@@ -1,5 +1,6 @@
 /* Copyright (c) 2009, 2010 Simon Josefsson <simon_at_josefsson.org>
  * Copyright (c) 2004-2007, Sara Golemon <sarag_at_libssh2.org>
+ * Copyright (c) 2018 Peter Stuge
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms,
@@ -117,6 +118,148 @@ crypt_dtor(LIBSSH2_SESSION * session, void **abstract)
     return 0;
 }
 
+#if ( \
+ (defined(_libssh2_cipher_aes256) && !defined(_libssh2_cipher_aes256ctr)) || \
+ (defined(_libssh2_cipher_aes192) && !defined(_libssh2_cipher_aes192ctr)) || \
+ (defined(_libssh2_cipher_aes128) && !defined(_libssh2_cipher_aes128ctr)) || \
+ 0) /* implement CTR mode using CBC */
+
+struct crypt_ctr_ctx {
+    const LIBSSH2_CRYPT_METHOD *method;
+    unsigned char *ctr;
+    unsigned char *tmp;
+    unsigned char *zero_iv;
+    unsigned char *secret;
+    _libssh2_cipher_ctx h;
+};
+
+static int crypt_ctr_init(LIBSSH2_SESSION *session,
+                          const LIBSSH2_CRYPT_METHOD *method,
+                          unsigned char *iv, int *free_iv,
+                          unsigned char *secret, int *free_secret,
+                          int encrypt, void **abstract)
+{
+    struct crypt_ctr_ctx *ctx;
+
+    (void)encrypt;
+
+    if (method->blocksize < 1 || method->iv_len < 1 || method->secret_len < 1)
+        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+
+    if (method->blocksize != method->iv_len)
+        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+
+    ctx = LIBSSH2_ALLOC(session, sizeof *ctx);
+    if (NULL == ctx)
+        goto err;
+
+    ctx->ctr = LIBSSH2_ALLOC(session, method->blocksize);
+    if (NULL == ctx->ctr)
+        goto err;
+
+    ctx->tmp = LIBSSH2_ALLOC(session, method->blocksize);
+    if (NULL == ctx->tmp)
+        goto err;
+
+    ctx->zero_iv = LIBSSH2_ALLOC(session, method->iv_len);
+    if (NULL == ctx->zero_iv)
+        goto err;
+
+    ctx->secret = LIBSSH2_ALLOC(session, method->secret_len);
+    if (NULL == ctx->secret)
+        goto err;
+
+    ctx->method = method;
+    memcpy(ctx->ctr, iv, method->blocksize);
+    memset(ctx->zero_iv, 0, method->iv_len);
+    memcpy(ctx->secret, secret, method->secret_len);
+
+    *abstract = ctx;
+    *free_iv = 1;
+    *free_secret = 1;
+    return 0;
+
+err:
+    if (ctx) {
+        if (ctx->secret)
+            LIBSSH2_FREE(session, ctx->secret);
+        if (ctx->zero_iv)
+            LIBSSH2_FREE(session, ctx->zero_iv);
+        if (ctx->tmp)
+            LIBSSH2_FREE(session, ctx->tmp);
+        if (ctx->ctr)
+            LIBSSH2_FREE(session, ctx->ctr);
+
+        LIBSSH2_FREE(session, ctx);
+    }
+
+    return LIBSSH2_ERROR_ALLOC;
+}
+
+static int crypt_ctr_encrypt(LIBSSH2_SESSION *session,
+                             unsigned char *block, size_t blocksize,
+                             void **abstract)
+{
+    struct crypt_ctr_ctx *ctx = *abstract;
+    size_t n, p;
+    int ret;
+
+    (void)session;
+
+    for (n = 0; n < blocksize; n++) {
+        p = n % ctx->method->blocksize;
+
+        if (0 == p) {
+            /* use CBC with zero IV on a single block to emulate ECB */
+
+            ret = _libssh2_cipher_init(&ctx->h, ctx->method->algo,
+                                       ctx->zero_iv, ctx->secret, 1);
+            if (ret)
+                return ret;
+
+            memcpy(ctx->tmp, ctx->ctr, ctx->method->blocksize);
+
+            ret = _libssh2_cipher_crypt(&ctx->h, ctx->method->algo, 1,
+                                        ctx->tmp, ctx->method->blocksize);
+            _libssh2_cipher_dtor(&ctx->h);
+            if (ret)
+                return ret;
+
+            _libssh2_aes_ctr_increment(ctx->ctr, ctx->method->blocksize);
+        }
+
+        block[n] ^= ctx->tmp[p];
+    }
+
+    return 0;
+}
+
+static int crypt_ctr_dtor(LIBSSH2_SESSION *session, void **abstract)
+{
+    struct crypt_ctr_ctx *ctx;
+
+    if (!abstract || !*abstract)
+        return 0;
+
+    ctx = *abstract;
+
+    memset(ctx->ctr, 0, ctx->method->blocksize);
+    memset(ctx->tmp, 0, ctx->method->blocksize);
+    memset(ctx->secret, 0, ctx->method->secret_len);
+
+    LIBSSH2_FREE(session, ctx->secret);
+    LIBSSH2_FREE(session, ctx->zero_iv);
+    LIBSSH2_FREE(session, ctx->tmp);
+    LIBSSH2_FREE(session, ctx->ctr);
+
+    memset(ctx, 0, sizeof *ctx);
+    LIBSSH2_FREE(session, ctx);
+
+    *abstract = NULL;
+    return 0;
+}
+#endif /* implement CTR mode using CBC */
+
 #ifdef _libssh2_cipher_aes128
 static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = {
     "aes128-cbc",
@@ -130,9 +273,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = {
     &crypt_dtor,
     _libssh2_cipher_aes128
 };
-#endif
 
-#ifdef _libssh2_cipher_aes128ctr
 static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = {
     "aes128-ctr",
     "",
@@ -140,10 +281,17 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = {
     16,                         /* initial value length */
     16,                         /* secret length -- 16*8 == 128bit */
     0,                          /* flags */
+#ifdef _libssh2_cipher_aes128ctr
     &crypt_init,
     &crypt_encrypt,
     &crypt_dtor,
     _libssh2_cipher_aes128ctr
+#else
+    &crypt_ctr_init,
+    &crypt_ctr_encrypt,
+    &crypt_ctr_dtor,
+    _libssh2_cipher_aes128
+#endif
 };
 #endif
 
@@ -160,9 +308,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = {
     &crypt_dtor,
     _libssh2_cipher_aes192
 };
-#endif
 
-#ifdef _libssh2_cipher_aes192ctr
 static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = {
     "aes192-ctr",
     "",
@@ -170,10 +316,17 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = {
     16,                         /* initial value length */
     24,                         /* secret length -- 24*8 == 192bit */
     0,                          /* flags */
+#ifdef _libssh2_cipher_aes192ctr
     &crypt_init,
     &crypt_encrypt,
     &crypt_dtor,
     _libssh2_cipher_aes192ctr
+#else
+    &crypt_ctr_init,
+    &crypt_ctr_encrypt,
+    &crypt_ctr_dtor,
+    _libssh2_cipher_aes192
+#endif
 };
 #endif
 
@@ -205,9 +358,7 @@ static const LIBSSH2_CRYPT_METHOD
     &crypt_dtor,
     _libssh2_cipher_aes256
 };
-#endif
 
-#ifdef _libssh2_cipher_aes256ctr
 static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = {
     "aes256-ctr",
     "",
@@ -215,10 +366,17 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = {
     16,                         /* initial value length */
     32,                         /* secret length -- 32*8 == 256bit */
     0,                          /* flags */
+#ifdef _libssh2_cipher_aes256ctr
     &crypt_init,
     &crypt_encrypt,
     &crypt_dtor,
     _libssh2_cipher_aes256ctr
+#else
+    &crypt_ctr_init,
+    &crypt_ctr_encrypt,
+    &crypt_ctr_dtor,
+    _libssh2_cipher_aes256
+#endif
 };
 #endif
 
@@ -319,23 +477,17 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = {
 #endif
 
 static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
-#ifdef _libssh2_cipher_aes256ctr
-  &libssh2_crypt_method_aes256_ctr,
-#endif
 #ifdef _libssh2_cipher_aes256
+    &libssh2_crypt_method_aes256_ctr,
     &libssh2_crypt_method_aes256_cbc,
     &libssh2_crypt_method_rijndael_cbc_lysator_liu_se,  /* == aes256-cbc */
 #endif
-#ifdef _libssh2_cipher_aes192ctr
-  &libssh2_crypt_method_aes192_ctr,
-#endif
 #ifdef _libssh2_cipher_aes192
+    &libssh2_crypt_method_aes192_ctr,
     &libssh2_crypt_method_aes192_cbc,
 #endif
-#ifdef _libssh2_cipher_aes128ctr
-  &libssh2_crypt_method_aes128_ctr,
-#endif
 #ifdef _libssh2_cipher_aes128
+    &libssh2_crypt_method_aes128_ctr,
     &libssh2_crypt_method_aes128_cbc,
 #endif
 #if LIBSSH2_BLOWFISH
-- 
_______________________________________________
libssh2-devel https://cool.haxx.se/cgi-bin/mailman/listinfo/libssh2-devel
Received on 2018-04-15