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)
/* 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)
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,
- 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;
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);
- bool sideband_requested = false;
*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)
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;
}
}
}
- 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;
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();
/* 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;
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 (!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),
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;
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;
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <openssl/bn.h>
+#include <openssl/aes.h>
#include "para.h"
#include "error.h"
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 {
- 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));
- 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;
}
*/
#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;
- RC4_KEY *key = &sc->key;
assert(len > 0);
assert(len < ((typeof(src->iov_len))-1) / 2);
((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;
*
* \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);
+struct stream_cipher *sc_new(const unsigned char *data, int len,
+ bool use_aes);
/**
* Encrypt or decrypt a buffer using a stream cipher.
/* 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);
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;
-
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) {
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
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).
-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,
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
- 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
-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