crypt: Remove RC4 support.
authorAndre Noll <maan@tuebingen.mpg.de>
Wed, 24 Aug 2016 13:12:53 +0000 (15:12 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 8 Jan 2017 12:27:16 +0000 (13:27 +0100)
Multiple vulnerabilities have been discovered in the RC4 stream cipher,
rendering it insecure. paraslash stopped using RC4 as the default
stream cipher since version 0.5.2 (2014-04-11), but server and client
still supported the broken cipher for backward compatibility. This
commit removes the compatibility code from both the openssl and
the libgcrypt code base, leaving aes_ctr128 as the only remaining
stream cipher.

The server still announces the aes_ctr128 feature, although it is now
mandatory because the server will enable aes_ctr128 unconditionally,
no matter whether it was requested by the client or not. The client,
on the other hand, still requests this feature, regardless of whether
it was announced by the server or not. This keeps unpatched clients =>
0.5.2 working with new servers and vice versa.

Regarding the public crypto API, sc_new() loses its boolean use_aes
parameter. Otherwise the API remains the same.

The patch also rewrites the crypto section of the manual to not
mention RC4 any more.

client_common.c
command.c
crypt.c
crypt.h
gcrypt.c
web/manual.md

index eea7510..ac53b9b 100644 (file)
@@ -241,11 +241,6 @@ out:
        return ret;
 }
 
-static bool has_feature(const char *feature, struct client_task *ct)
-{
-       return find_arg(feature, ct->features) >= 0? true : false;
-}
-
 static int send_sb_command(struct client_task *ct)
 {
        int i;
@@ -297,8 +292,8 @@ static int client_post_select(struct sched *s, void *context)
        case CL_RECEIVED_WELCOME: /* send auth command */
                if (!FD_ISSET(ct->scc.fd, &s->wfds))
                        return 0;
-               sprintf(buf, AUTH_REQUEST_MSG "%s sideband%s", ct->user,
-                       has_feature("aes_ctr128", ct)? ",aes_ctr128" : "");
+               sprintf(buf, AUTH_REQUEST_MSG "%s sideband,aes_ctr128",
+                       ct->user);
                PARA_INFO_LOG("--> %s\n", buf);
                ret = write_buffer(ct->scc.fd, buf);
                if (ret < 0)
@@ -314,7 +309,6 @@ static int client_post_select(struct sched *s, void *context)
                /* 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)
@@ -333,10 +327,9 @@ static int client_post_select(struct sched *s, void *context)
                        goto out;
                ct->challenge_hash = para_malloc(HASH_SIZE);
                hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
-               use_aes = has_feature("aes_ctr128", ct);
-               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, use_aes);
+               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
                ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
-                       SESSION_KEY_LEN, use_aes);
+                       SESSION_KEY_LEN);
                hash_to_asc(ct->challenge_hash, buf);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
index fe4b923..58441b0 100644 (file)
--- a/command.c
+++ b/command.c
@@ -807,7 +807,7 @@ static void reset_signals(void)
 }
 
 struct connection_features {
-       bool aes_ctr128_requested;
+       int dummy; /* none at the moment */
 };
 
 static int parse_auth_request(char *buf, int len, struct user **u,
@@ -836,7 +836,7 @@ static int parse_auth_request(char *buf, int len, struct user **u,
                        if (strcmp(features[i], "sideband") == 0)
                                continue;
                        if (strcmp(features[i], "aes_ctr128") == 0)
-                               cf->aes_ctr128_requested = true;
+                               continue;
                        else {
                                ret = -E_BAD_FEATURE;
                                goto out;
@@ -994,10 +994,9 @@ __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 */
-       cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN,
-               cf.aes_ctr128_requested);
+       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, cf.aes_ctr128_requested);
+               SESSION_KEY_LEN);
        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 8116fb6..cae4704 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -11,7 +11,6 @@
 #include <sys/socket.h>
 #include <openssl/rand.h>
 #include <openssl/err.h>
-#include <openssl/rc4.h>
 #include <openssl/pem.h>
 #include <openssl/sha.h>
 #include <openssl/bn.h>
@@ -264,26 +263,16 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
 }
 
 struct stream_cipher {
-       bool use_aes;
-       union {
-               RC4_KEY rc4_key;
-               EVP_CIPHER_CTX *aes;
-       } context;
+       EVP_CIPHER_CTX *aes;
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len,
-               bool use_aes)
+struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
 
-       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);
-       sc->context.aes = EVP_CIPHER_CTX_new();
-       EVP_EncryptInit_ex(sc->context.aes, EVP_aes_128_ctr(), NULL, data,
+       sc->aes = EVP_CIPHER_CTX_new();
+       EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
                data + AES_CRT128_BLOCK_SIZE);
        return sc;
 }
@@ -292,40 +281,10 @@ void sc_free(struct stream_cipher *sc)
 {
        if (!sc)
                return;
-       EVP_CIPHER_CTX_free(sc->context.aes);
+       EVP_CIPHER_CTX_free(sc->aes);
        free(sc);
 }
 
-/**
- * The RC4() implementation of openssl apparently reads and writes data in
- * blocks of 8 bytes. So we have to make sure our buffer sizes are a multiple
- * of this.
- */
-#define RC4_ALIGN 8
-
-static void rc4_crypt(RC4_KEY *key, struct iovec *src, struct iovec *dst)
-{
-       size_t len = src->iov_len, l1, l2;
-
-       assert(len > 0);
-       assert(len < ((typeof(src->iov_len))-1) / 2);
-       l1 = ROUND_DOWN(len, RC4_ALIGN);
-       l2 = ROUND_UP(len, RC4_ALIGN);
-
-       *dst = (typeof(*dst)) {
-               /* Add one for the terminating zero byte. */
-               .iov_base = para_malloc(l2 + 1),
-               .iov_len = len
-       };
-       RC4(key, l1, src->iov_base, dst->iov_base);
-       if (len > l1) {
-               unsigned char remainder[RC4_ALIGN] = "";
-               memcpy(remainder, src->iov_base + l1, len - l1);
-               RC4(key, len - l1, remainder, dst->iov_base + l1);
-       }
-       ((char *)dst->iov_base)[len] = '\0';
-}
-
 static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
                struct iovec *dst)
 {
@@ -347,9 +306,7 @@ static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
 
 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);
+       return aes_ctr128_crypt(sc->aes, src, dst);
 }
 
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
diff --git a/crypt.h b/crypt.h
index 9be7a23..f5f8aca 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -119,16 +119,14 @@ struct stream_cipher_context {
 };
 
 /**
- * Allocate and initialize a stream cipher structure.
+ * Allocate and initialize an aes_ctr128 stream cipher structure.
  *
  * \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.
  */
-struct stream_cipher *sc_new(const unsigned char *data, int len,
-               bool use_aes);
+struct stream_cipher *sc_new(const unsigned char *data, int len);
 
 /**
  * Encrypt or decrypt a buffer using a stream cipher.
index 7c19aeb..51b1b1f 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -912,33 +912,20 @@ struct stream_cipher {
        gcry_cipher_hd_t handle;
 };
 
-struct stream_cipher *sc_new(const unsigned char *data, int len,
-               bool use_aes)
+struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        gcry_error_t gret;
        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) {
-               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
-               free(sc);
-               return NULL;
-       }
-       gret = gcry_cipher_setkey(sc->handle, data, (size_t)len);
+       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;
 }
index 12454ee..461886a 100644 (file)
@@ -448,9 +448,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
-a stream cipher, either RC4 or AES in integer counter mode.
+the AES stream cipher in integer counter mode.
 
-In this chapter we briefly describe RSA, RC4 and AES, and sketch the
+In this chapter we briefly describe RSA and AES, and sketch the
 [authentication handshake](#Client-server.authentication)
 between para_client and para_server. User management is discussed
 in the section on [the user_list file](#The.user_list.file).
@@ -458,33 +458,33 @@ These sections are all about communication between the client and the
 server. Connecting para_audiod is a different matter and is described
 in a [separate section](#Connecting.para_audiod).
 
-RSA, RC4, AES
--------------
+RSA and 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,
+A block cipher is a transformation which operates on fixed-length
+blocks. For symmetric block ciphers the transformation is determined
+by a single key for both encryption and decryption. For asymmetric
+block ciphers, on the other hand, the key consists of two parts,
 called the public key and the private key. A message can be encrypted
-with either key and only the counterpart of that key can decrypt
-the message. While RSA can be used for both signing and encrypting
-a message, paraslash uses RSA only for the latter purpose. The
-RSA public key encryption and signatures algorithms are defined in
-detail in RFC 2437.
-
-RC4 is a stream cipher, i.e. the input is XORed with a pseudo-random
-key stream to produce the output. Decryption uses the same function
-calls as encryption. While RC4 supports variable key lengths,
-paraslash uses a fixed length of 256 bits, which is considered a
-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.
+with either key and only the counterpart of that key can decrypt the
+message. Asymmetric block ciphers can be used for both signing and
+encrypting a message.
+
+RSA is an asymmetric block cipher which is used in many applications,
+including ssh and gpg. The RSA public key encryption and signatures
+algorithms are defined in detail in RFC 2437. Paraslash relies on
+RSA for authentication.
+
+Stream ciphers XOR the input with a pseudo-random key stream to produce
+the output. Decryption uses the same function calls as encryption.
+Any block cipher can be turned into a stream cipher by generating the
+pseudo-random key stream by encrypting successive values of a counter
+(counter mode).
 
 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.
+cipher. Paraslash employs AES in counter mode as described above to
+encrypt communications. Since a stream cipher key must not be used
+twice, a random key is generated for every new connection.
 
 Client-server authentication
 ----------------------------
@@ -524,8 +524,8 @@ 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 the
-implementation of the RSA, RC4 and AES crypto routines and on the
+by the crypto library (openssl or libgcrypt), on the security of
+the implementation of the RSA and AES crypto routines and on the
 infeasibility to invert the SHA1 function.
 
 Neither para_server or para_client create RSA keys on their