]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Implement aes_ctr128 and prefer it over RC4.
authorAndre Noll <maan@systemlinux.org>
Mon, 2 Dec 2013 21:18:57 +0000 (22:18 +0100)
committerAndre Noll <maan@systemlinux.org>
Thu, 9 Jan 2014 16:18:59 +0000 (16:18 +0000)
This adds the aes_ctr128 stream cipher to para_server, para_client
and para_audiod. para_server now announces this cipher as a supported
feature and para_client requests an aes_ctr128-encrypted session
if the server supports it. If one or both sides don't understand
the new feature, the RC4 fallback applies. So this change is
backwards-compatible.

Both gcrypt and openssl support aes_ctr128, and the two implementations
are compatible to each other: a para_client linked against openssl can
talk to a para_server which was linked against libgcrypt and vice versa.

This patch also updates the documentation to talk about stream
ciphers and session keys rather than to mention RC4 explicitly. A
short section on AES is added which also explains how counter mode
turns a block cipher into a stream cipher.

client_common.c
command.c
crypt.c
crypt.h
crypt_backend.h
gcrypt.c
web/manual.m4

index c19b71218b59f62f1e6a75c1eee43ee332a65900..54b217129eaf13fc289da5b1f861c29b6ae8637e 100644 (file)
@@ -331,7 +331,8 @@ static int client_post_select(struct sched *s, struct task *t)
        case CL_RECEIVED_WELCOME: /* send auth command */
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return 0;
        case CL_RECEIVED_WELCOME: /* send auth command */
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return 0;
-               sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
+               sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
+                       has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
@@ -347,6 +348,7 @@ static int client_post_select(struct sched *s, struct task *t)
                /* decrypted challenge/session key buffer */
                unsigned char crypt_buf[1024];
                struct sb_buffer sbb;
                /* decrypted challenge/session key buffer */
                unsigned char crypt_buf[1024];
                struct sb_buffer sbb;
+               bool use_aes;
 
                ret = recv_sb(ct, &s->rfds, &sbb);
                if (ret <= 0)
 
                ret = recv_sb(ct, &s->rfds, &sbb);
                if (ret <= 0)
@@ -365,9 +367,10 @@ static int client_post_select(struct sched *s, struct task *t)
                        goto out;
                ct->challenge_hash = para_malloc(HASH_SIZE);
                hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
                        goto out;
                ct->challenge_hash = para_malloc(HASH_SIZE);
                hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
-               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
+               use_aes = has_feature("aes_ctr128", ct);
+               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, use_aes);
                ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
                ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
-                       SESSION_KEY_LEN);
+                       SESSION_KEY_LEN, use_aes);
                hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
                hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
index 34d6841a6779512a1fc490f918f3fbce9c9393fc..0bd5c234ba9b652aa78e104f204d508e625cd26e 100644 (file)
--- a/command.c
+++ b/command.c
@@ -780,14 +780,20 @@ static void reset_signals(void)
        para_sigaction(SIGHUP, SIG_DFL);
 }
 
        para_sigaction(SIGHUP, SIG_DFL);
 }
 
-static int parse_auth_request(char *buf, int len, struct user **u)
+struct connection_features {
+       bool sideband_requested;
+       bool aes_ctr128_requested;
+};
+
+static int parse_auth_request(char *buf, int len, struct user **u,
+               struct connection_features *cf)
 {
        int ret;
        char *p, *username, **features = NULL;
        size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
 {
        int ret;
        char *p, *username, **features = NULL;
        size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
-       bool sideband_requested = false;
 
        *u = NULL;
 
        *u = NULL;
+       memset(cf, 0, sizeof(*cf));
        if (len < auth_rq_len + 2)
                return -E_AUTH_REQUEST;
        if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0)
        if (len < auth_rq_len + 2)
                return -E_AUTH_REQUEST;
        if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0)
@@ -803,18 +809,15 @@ static int parse_auth_request(char *buf, int len, struct user **u)
                create_argv(p, ",", &features);
                for (i = 0; features[i]; i++) {
                        if (strcmp(features[i], "sideband") == 0)
                create_argv(p, ",", &features);
                for (i = 0; features[i]; i++) {
                        if (strcmp(features[i], "sideband") == 0)
-                               sideband_requested = true;
+                               cf->sideband_requested = true;
+                       else if (strcmp(features[i], "aes_ctr128") == 0)
+                               cf->aes_ctr128_requested = true;
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
                        }
                }
        }
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
                        }
                }
        }
-       if (sideband_requested == false) { /* sideband is mandatory */
-               PARA_ERROR_LOG("client did not request sideband\n");
-               ret = -E_BAD_FEATURE;
-               goto out;
-       }
        PARA_DEBUG_LOG("received auth request for user %s\n", username);
        *u = lookup_user(username);
        ret = 1;
        PARA_DEBUG_LOG("received auth request for user %s\n", username);
        *u = lookup_user(username);
        ret = 1;
@@ -893,6 +896,7 @@ __noreturn void handle_connect(int fd, const char *peername)
        size_t numbytes;
        struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct;
        struct iovec iov;
        size_t numbytes;
        struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct;
        struct iovec iov;
+       struct connection_features cf;
 
        cc->scc.fd = fd;
        reset_signals();
 
        cc->scc.fd = fd;
        reset_signals();
@@ -903,7 +907,7 @@ __noreturn void handle_connect(int fd, const char *peername)
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
                PACKAGE_VERSION  ".\n"
        /* send Welcome message */
        ret = write_va_buffer(fd, "This is para_server, version "
                PACKAGE_VERSION  ".\n"
-               "Features: sideband\n"
+               "Features: sideband,aes_ctr128\n"
        );
        if (ret < 0)
                goto net_err;
        );
        if (ret < 0)
                goto net_err;
@@ -911,9 +915,14 @@ __noreturn void handle_connect(int fd, const char *peername)
        ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
        ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
-       ret = parse_auth_request(buf, ret, &cc->u);
+       ret = parse_auth_request(buf, ret, &cc->u, &cf);
        if (ret < 0)
                goto net_err;
        if (ret < 0)
                goto net_err;
+       if (!cf.sideband_requested) { /* sideband is mandatory */
+               PARA_ERROR_LOG("client did not request sideband\n");
+               ret = -E_BAD_FEATURE;
+               goto net_err;
+       }
        if (cc->u) {
                get_random_bytes_or_die(rand_buf, sizeof(rand_buf));
                ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
        if (cc->u) {
                get_random_bytes_or_die(rand_buf, sizeof(rand_buf));
                ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
@@ -930,7 +939,7 @@ __noreturn void handle_connect(int fd, const char *peername)
                numbytes = 256;
                get_random_bytes_or_die((unsigned char *)buf, numbytes);
        }
                numbytes = 256;
                get_random_bytes_or_die((unsigned char *)buf, numbytes);
        }
-       PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
+       PARA_DEBUG_LOG("sending %u byte challenge + session key (%zu bytes)\n",
                CHALLENGE_SIZE, numbytes);
        ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
        buf = NULL;
                CHALLENGE_SIZE, numbytes);
        ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
        buf = NULL;
@@ -960,8 +969,10 @@ __noreturn void handle_connect(int fd, const char *peername)
        alarm(0);
        PARA_INFO_LOG("good auth for %s\n", cc->u->name);
        /* init stream cipher keys with the second part of the random buffer */
        alarm(0);
        PARA_INFO_LOG("good auth for %s\n", cc->u->name);
        /* init stream cipher keys with the second part of the random buffer */
-       cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
-       cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN);
+       cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN,
+               cf.aes_ctr128_requested);
+       cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
+               SESSION_KEY_LEN, cf.aes_ctr128_requested);
        ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
        if (ret < 0)
                goto net_err;
        ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
        if (ret < 0)
                goto net_err;
diff --git a/crypt.c b/crypt.c
index 5445138d53f6589ebce6645b027531f02b74c860..e6a31e0e121f9235804664a303c742339caedec4 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -15,6 +15,7 @@
 #include <openssl/pem.h>
 #include <openssl/sha.h>
 #include <openssl/bn.h>
 #include <openssl/pem.h>
 #include <openssl/sha.h>
 #include <openssl/bn.h>
+#include <openssl/aes.h>
 
 #include "para.h"
 #include "error.h"
 
 #include "para.h"
 #include "error.h"
@@ -259,14 +260,40 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
        return ret < 0? -E_ENCRYPT : ret;
 }
 
        return ret < 0? -E_ENCRYPT : ret;
 }
 
+struct aes_ctr_128_context {
+       AES_KEY key;
+       unsigned char ivec[AES_CRT128_BLOCK_SIZE];
+       unsigned char ecount[AES_CRT128_BLOCK_SIZE];
+       unsigned int num;
+};
+
 struct stream_cipher {
 struct stream_cipher {
-       RC4_KEY key;
+       bool use_aes;
+       union {
+               RC4_KEY rc4_key;
+               struct aes_ctr_128_context aes;
+       } context;
 };
 
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len)
+struct stream_cipher *sc_new(const unsigned char *data, int len,
+               bool use_aes)
 {
 {
+       int ret;
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
-       RC4_set_key(&sc->key, len, data);
+       struct aes_ctr_128_context *aes;
+
+       sc->use_aes = use_aes;
+       if (!use_aes) {
+               RC4_set_key(&sc->context.rc4_key, len, data);
+               return sc;
+       }
+       assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
+       aes = &sc->context.aes;
+       ret = AES_set_encrypt_key(data, AES_CRT128_BLOCK_SIZE * 8 /* bits */,
+               &aes->key);
+       assert(ret == 0);
+       memcpy(aes->ivec, data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE);
+       aes->num = 0;
        return sc;
 }
 
        return sc;
 }
 
@@ -282,10 +309,9 @@ void sc_free(struct stream_cipher *sc)
  */
 #define RC4_ALIGN 8
 
  */
 #define RC4_ALIGN 8
 
-void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+static void rc4_crypt(RC4_KEY *key, struct iovec *src, struct iovec *dst)
 {
        size_t len = src->iov_len, l1, l2;
 {
        size_t len = src->iov_len, l1, l2;
-       RC4_KEY *key = &sc->key;
 
        assert(len > 0);
        assert(len < ((typeof(src->iov_len))-1) / 2);
 
        assert(len > 0);
        assert(len < ((typeof(src->iov_len))-1) / 2);
@@ -306,6 +332,28 @@ void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
        ((char *)dst->iov_base)[len] = '\0';
 }
 
        ((char *)dst->iov_base)[len] = '\0';
 }
 
+static void aes_ctr128_crypt(struct aes_ctr_128_context *aes, struct iovec *src,
+               struct iovec *dst)
+{
+       size_t len = src->iov_len;
+
+       *dst = (typeof(*dst)) {
+               /* Add one for the terminating zero byte. */
+               .iov_base = para_malloc(len + 1),
+               .iov_len = len
+       };
+       AES_ctr128_encrypt(src->iov_base, dst->iov_base, len,
+               &aes->key, aes->ivec, aes->ecount, &aes->num);
+       ((char *)dst->iov_base)[len] = '\0';
+}
+
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+{
+       if (sc->use_aes)
+               return aes_ctr128_crypt(&sc->context.aes, src, dst);
+       return rc4_crypt(&sc->context.rc4_key, src, dst);
+}
+
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
 {
        SHA_CTX c;
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
 {
        SHA_CTX c;
diff --git a/crypt.h b/crypt.h
index 1406197d0c6cd64c14cf1b30d8ef281417903638..e9657ff5c0ae2b9c5a8875ea9f37e8e145ff9f64 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -123,10 +123,12 @@ struct stream_cipher_context {
  *
  * \param data The key.
  * \param len The size of the key.
  *
  * \param data The key.
  * \param len The size of the key.
+ * \param use_aes True: Use the aes_ctr128 stream cipher, false: Use RC4.
  *
  * \return A new stream cipher structure.
  */
  *
  * \return A new stream cipher structure.
  */
-struct stream_cipher *sc_new(const unsigned char *data, int len);
+struct stream_cipher *sc_new(const unsigned char *data, int len,
+               bool use_aes);
 
 /**
  * Encrypt or decrypt a buffer using a stream cipher.
 
 /**
  * Encrypt or decrypt a buffer using a stream cipher.
index 481a215fb24bc4257c1faebde329c07fa1d7ff1e..3676fff1b1a7d822dcaaefaec62c0f64bd90b3db 100644 (file)
@@ -8,6 +8,9 @@
 
 /* This should only be incuded from files which provide crypto functions. */
 
 
 /* This should only be incuded from files which provide crypto functions. */
 
+/** AES block size in bytes. */
+#define AES_CRT128_BLOCK_SIZE 16
+
 size_t is_ssh_rsa_key(char *data, size_t size);
 uint32_t read_ssh_u32(const void *vp);
 int uudecode(const char *src, unsigned char *target, size_t targsize);
 size_t is_ssh_rsa_key(char *data, size_t size);
 uint32_t read_ssh_u32(const void *vp);
 int uudecode(const char *src, unsigned char *target, size_t targsize);
index 2736a6c769835823b39820b0291069f1ca80a534..2a1a90d357dc3fd3cc81803bd0a8ce46a2734f71 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -912,11 +912,25 @@ struct stream_cipher {
        gcry_cipher_hd_t handle;
 };
 
        gcry_cipher_hd_t handle;
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len)
+struct stream_cipher *sc_new(const unsigned char *data, int len,
+               bool use_aes)
 {
        gcry_error_t gret;
 {
        gcry_error_t gret;
-
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
+
+       if (use_aes) {
+               assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
+               gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128,
+                       GCRY_CIPHER_MODE_CTR, 0);
+               assert(gret == 0);
+               gret = gcry_cipher_setkey(sc->handle, data,
+                       AES_CRT128_BLOCK_SIZE);
+               assert(gret == 0);
+               gret = gcry_cipher_setctr(sc->handle,
+                       data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE);
+               assert(gret == 0);
+               return sc;
+       }
        gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_ARCFOUR,
                GCRY_CIPHER_MODE_STREAM, 0);
        if (gret) {
        gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_ARCFOUR,
                GCRY_CIPHER_MODE_STREAM, 0);
        if (gret) {
index 62ebf3a8e5f897135fc20e1d754e1e6ce6ff5436..20cae69ab7e00d6d50b8f5026ab5c42d8e39c33b 100644 (file)
@@ -112,7 +112,7 @@ can be used by any scripting language to produce user interfaces with
 little programming effort.
 
 All connections between para_server and para_client are encrypted
 little programming effort.
 
 All connections between para_server and para_client are encrypted
-with a symmetric RC4 session key. For each user of paraslash you must
+with a symmetric session key. For each user of paraslash you must
 create a public/secret RSA key pair for authentication.
 
 If para_client is started without non-option arguments, an interactive
 create a public/secret RSA key pair for authentication.
 
 If para_client is started without non-option arguments, an interactive
@@ -505,9 +505,9 @@ User management
 para_server uses a challenge-response mechanism to authenticate
 requests from incoming connections, similar to ssh's public key
 authentication method. Authenticated connections are encrypted using
 para_server uses a challenge-response mechanism to authenticate
 requests from incoming connections, similar to ssh's public key
 authentication method. Authenticated connections are encrypted using
-the RC4 stream cipher.
+a stream cipher, either RC4 or AES in integer counter mode.
 
 
-In this chapter we briefly describe RSA and RC4 and sketch the
+In this chapter we briefly describe RSA, RC4 and AES, and sketch the
 REFERENCE(Client-server authentication, authentication handshake)
 between para_client and para_server. User management is discussed
 in the section on REFERENCE(The user_list file, the user_list file).
 REFERENCE(Client-server authentication, authentication handshake)
 between para_client and para_server. User management is discussed
 in the section on REFERENCE(The user_list file, the user_list file).
@@ -517,8 +517,8 @@ in a REFERENCE(Connecting para_audiod, separate section).
 
 
 
 
 
 
-RSA and RC4
-~~~~~~~~~~~
+RSA, RC4, AES
+~~~~~~~~~~~~~
 
 RSA is an asymmetric block cipher which is used in many applications,
 including ssh and gpg. An RSA key consists in fact of two keys,
 
 RSA is an asymmetric block cipher which is used in many applications,
 including ssh and gpg. An RSA key consists in fact of two keys,
@@ -537,6 +537,15 @@ strong encryption by today's standards. Since the same key must never
 be used twice, a different, randomly-generated key is used for every
 new connection.
 
 be used twice, a different, randomly-generated key is used for every
 new connection.
 
+AES, the advanced encryption standard, is a well-known symmetric block
+cipher, i.e. a transformation operating on fixed-length blocks which
+is determined by a single key for both encryption and decryption. Any
+block cipher can be turned into a stream cipher by generating
+a pseudo-random key stream by encrypting successive values of a
+counter. The AES_CTR128 stream cipher used in paraslash is obtained
+in this way from the AES block cipher with a 128 bit block size.
+
+
 Client-server authentication
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Client-server authentication
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -557,7 +566,7 @@ as follows:
        fixed-length buffer with random bytes, encrypts that buffer
        using the public key and sends the encrypted buffer to the
        client. The first part of the buffer is the challenge which
        fixed-length buffer with random bytes, encrypts that buffer
        using the public key and sends the encrypted buffer to the
        client. The first part of the buffer is the challenge which
-       is used for authentication while the second part is the RC4
+       is used for authentication while the second part is the
        session key.
 
        - para_client receives the encrypted buffer and decrypts it
        session key.
 
        - para_client receives the encrypted buffer and decrypts it
@@ -574,12 +583,12 @@ as follows:
 
        - Otherwise the user is considered authenticated and the client
        is allowed to proceed by sending a command to be executed. From
 
        - Otherwise the user is considered authenticated and the client
        is allowed to proceed by sending a command to be executed. From
-       this point on the communication is encrypted using the RC4
-       stream cipher with the session key known to both peers.
+       this point on the communication is encrypted using the stream
+       cipher with the session key known to both peers.
 
 paraslash relies on the quality of the pseudo-random bytes provided
 by the crypto library (openssl or libgcrypt), on the security of
 
 paraslash relies on the quality of the pseudo-random bytes provided
 by the crypto library (openssl or libgcrypt), on the security of
-the implementation of the RSA and RC4 crypto routines and on the
+the implementation of the RSA, RC4 and AES crypto routines and on the
 infeasibility to invert the SHA1 function.
 
 Neither para_server or para_client create RSA keys on their own. This
 infeasibility to invert the SHA1 function.
 
 Neither para_server or para_client create RSA keys on their own. This