summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2019-08-02 19:12:34 +0200
committerSander Vrijders <sander@ouroboros.rocks>2019-08-03 12:10:57 +0200
commit9e8d603d14561095fb8d08871319a315d3bf6763 (patch)
tree7a87c212fcd642a8696145b4246a4fc4cf964e10
parent8a37ffbf8c0776a38f2de18a63e885383960ee68 (diff)
downloadouroboros-9e8d603d14561095fb8d08871319a315d3bf6763.tar.gz
ouroboros-9e8d603d14561095fb8d08871319a315d3bf6763.zip
lib: Add per-message encryption with OpenSSL
This adds a per-message symmetric encryption using the OpenSSL library. At flow allocation, an Elliptic Curve Diffie-Hellman exchange is performed to derive a shared secret, which is then hashed using SHA3-256 to be used as a key for symmetric AES-256 encryption. Each message on an encrypted flow adds a small crypto header that includes a random 128-bit Initialization Vector (IV). If the server does not have OpenSSL enabled, the flow allocation will fail with an -ECRYPT error. Future optimizations are to piggyback the public keys on the flow allocation message, and to enable per-flow encryption that maintains the context of the encryption over multiple packets and doesn't require sending IVs. Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks> Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
-rw-r--r--doc/man/flow_alloc.33
-rw-r--r--include/ouroboros/errno.h9
-rw-r--r--include/ouroboros/qos.h74
-rw-r--r--include/ouroboros/sockets.h.in4
-rw-r--r--src/ipcpd/eth/eth.c3
-rw-r--r--src/ipcpd/udp/main.c4
-rw-r--r--src/ipcpd/unicast/fa.c3
-rw-r--r--src/lib/config.h.in4
-rw-r--r--src/lib/crypt.c490
-rw-r--r--src/lib/dev.c121
-rw-r--r--src/lib/qosspec.proto1
-rw-r--r--src/lib/sockets.c6
12 files changed, 693 insertions, 29 deletions
diff --git a/doc/man/flow_alloc.3 b/doc/man/flow_alloc.3
index f4a6cf8d..48923fde 100644
--- a/doc/man/flow_alloc.3
+++ b/doc/man/flow_alloc.3
@@ -94,6 +94,9 @@ Not enough system memory resources available to allocate the flow.
.B -ETIMEDOUT
Flow allocation timed out.
+.B -ECRYPT
+The requested encryption is not supported.
+
.SH ATTRIBUTES
For an explanation of the terms used in this section, see \fBattributes\fR(7).
diff --git a/include/ouroboros/errno.h b/include/ouroboros/errno.h
index 27b21e82..20049751 100644
--- a/include/ouroboros/errno.h
+++ b/include/ouroboros/errno.h
@@ -25,11 +25,12 @@
#include <errno.h>
-#define ENOTALLOC 1000 /* Flow is not allocated */
-#define EIPCPTYPE 1001 /* Unknown IPCP type */
+#define ENOTALLOC 1000 /* Flow is not allocated */
+#define EIPCPTYPE 1001 /* Unknown IPCP type */
#define EIRMD 1002 /* Failed to communicate with IRMD */
#define EIPCP 1003 /* Failed to communicate with IPCP */
-#define EIPCPSTATE 1004 /* Target in wrong state */
-#define EFLOWDOWN 1005 /* Flow is down */
+#define EIPCPSTATE 1004 /* Target in wrong state */
+#define EFLOWDOWN 1005 /* Flow is down */
+#define ECRYPT 1006 /* Encryption error */
#endif /* OUROBOROS_ERRNO_H */
diff --git a/include/ouroboros/qos.h b/include/ouroboros/qos.h
index 0e4518f4..f5becaa6 100644
--- a/include/ouroboros/qos.h
+++ b/include/ouroboros/qos.h
@@ -34,6 +34,7 @@ typedef struct qos_spec {
uint32_t ber; /* Bit error rate, errors per billion bits */
uint8_t in_order; /* In-order delivery, enables FRCT */
uint32_t max_gap; /* In ms */
+ uint32_t cypher_s; /* Cypher strength, 0 = no encryption */
} qosspec_t;
static const qosspec_t qos_raw = {
@@ -43,7 +44,8 @@ static const qosspec_t qos_raw = {
.loss = 1,
.ber = 1,
.in_order = 0,
- .max_gap = UINT32_MAX
+ .max_gap = UINT32_MAX,
+ .cypher_s = 0
};
static const qosspec_t qos_raw_no_errors = {
@@ -53,7 +55,19 @@ static const qosspec_t qos_raw_no_errors = {
.loss = 1,
.ber = 0,
.in_order = 0,
- .max_gap = UINT32_MAX
+ .max_gap = UINT32_MAX,
+ .cypher_s = 0
+};
+
+static const qosspec_t qos_raw_crypt = {
+ .delay = UINT32_MAX,
+ .bandwidth = 0,
+ .availability = 0,
+ .loss = 1,
+ .ber = 0,
+ .in_order = 0,
+ .max_gap = UINT32_MAX,
+ .cypher_s = 256
};
static const qosspec_t qos_best_effort = {
@@ -63,7 +77,19 @@ static const qosspec_t qos_best_effort = {
.loss = 1,
.ber = 0,
.in_order = 1,
- .max_gap = UINT32_MAX
+ .max_gap = UINT32_MAX,
+ .cypher_s = 0
+};
+
+static const qosspec_t qos_best_effort_crypt = {
+ .delay = UINT32_MAX,
+ .bandwidth = 0,
+ .availability = 0,
+ .loss = 1,
+ .ber = 0,
+ .in_order = 1,
+ .max_gap = UINT32_MAX,
+ .cypher_s = 256
};
static const qosspec_t qos_video = {
@@ -73,7 +99,19 @@ static const qosspec_t qos_video = {
.loss = 1,
.ber = 0,
.in_order = 1,
- .max_gap = 100
+ .max_gap = 100,
+ .cypher_s = 0
+};
+
+static const qosspec_t qos_video_crypt = {
+ .delay = 100,
+ .bandwidth = UINT64_MAX,
+ .availability = 3,
+ .loss = 1,
+ .ber = 0,
+ .in_order = 1,
+ .max_gap = 100,
+ .cypher_s = 256
};
static const qosspec_t qos_voice = {
@@ -83,7 +121,19 @@ static const qosspec_t qos_voice = {
.loss = 1,
.ber = 0,
.in_order = 1,
- .max_gap = 50
+ .max_gap = 50,
+ .cypher_s = 0
+};
+
+static const qosspec_t qos_voice_crypt = {
+ .delay = 50,
+ .bandwidth = 100000,
+ .availability = 5,
+ .loss = 1,
+ .ber = 0,
+ .in_order = 1,
+ .max_gap = 50,
+ .cypher_s = 256
};
static const qosspec_t qos_data = {
@@ -93,7 +143,19 @@ static const qosspec_t qos_data = {
.loss = 0,
.ber = 0,
.in_order = 1,
- .max_gap = 2000
+ .max_gap = 2000,
+ .cypher_s = 0
+};
+
+static const qosspec_t qos_data_crypt = {
+ .delay = 1000,
+ .bandwidth = 0,
+ .availability = 0,
+ .loss = 0,
+ .ber = 0,
+ .in_order = 1,
+ .max_gap = 2000,
+ .cypher_s = 256
};
#endif /* OUROBOROS_QOS_H */
diff --git a/include/ouroboros/sockets.h.in b/include/ouroboros/sockets.h.in
index 1e9dc9ca..4f03ca46 100644
--- a/include/ouroboros/sockets.h.in
+++ b/include/ouroboros/sockets.h.in
@@ -60,8 +60,8 @@ irm_msg_t * send_recv_irm_msg(irm_msg_t * msg);
/* qos message conversion needed in different components */
-qosspec_msg_t spec_to_msg(qosspec_t * qs);
+qosspec_msg_t spec_to_msg(const qosspec_t * qs);
-qosspec_t msg_to_spec(qosspec_msg_t * msg);
+qosspec_t msg_to_spec(const qosspec_msg_t * msg);
#endif
diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c
index 1a332272..dccfd190 100644
--- a/src/ipcpd/eth/eth.c
+++ b/src/ipcpd/eth/eth.c
@@ -162,6 +162,7 @@ struct mgmt_msg {
uint32_t ber;
uint32_t max_gap;
uint32_t delay;
+ uint16_t cypher_s;
uint8_t in_order;
#if defined (BUILD_ETH_DIX)
uint8_t code;
@@ -485,6 +486,7 @@ static int eth_ipcp_alloc(const uint8_t * dst_addr,
msg->ber = hton32(qs.ber);
msg->in_order = qs.in_order;
msg->max_gap = hton32(qs.max_gap);
+ msg->cypher_s = hton16(qs.cypher_s);
memcpy(msg + 1, hash, ipcp_dir_hash_len());
@@ -731,6 +733,7 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf,
qs.ber = ntoh32(msg->ber);
qs.in_order = msg->in_order;
qs.max_gap = ntoh32(msg->max_gap);
+ qs.cypher_s = hton32(msg->cypher_s);
if (shim_data_reg_has(eth_data.shim_data,
buf + sizeof(*msg))) {
diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/main.c
index a1dcb602..f6aa57d1 100644
--- a/src/ipcpd/udp/main.c
+++ b/src/ipcpd/udp/main.c
@@ -91,6 +91,7 @@ struct mgmt_msg {
uint32_t loss;
uint32_t ber;
uint32_t max_gap;
+ uint32_t cypher_s;
} __attribute__((packed));
struct mgmt_frame {
@@ -209,6 +210,7 @@ static int ipcp_udp_port_alloc(int skfd,
msg->ber = hton32(qs.ber);
msg->in_order = qs.in_order;
msg->max_gap = hton32(qs.max_gap);
+ msg->cypher_s = hton32(qs.cypher_s);
memcpy(msg + 1, dst, ipcp_dir_hash_len());
@@ -383,6 +385,8 @@ static int ipcp_udp_mgmt_frame(const uint8_t * buf,
qs.ber = ntoh32(msg->ber);
qs.in_order = msg->in_order;
qs.max_gap = ntoh32(msg->max_gap);
+ qs.cypher_s = ntoh16(msg->cypher_s);
+
return ipcp_udp_port_req(&c_saddr, ntoh32(msg->s_eid),
(uint8_t *) (msg + 1), qs);
case FLOW_REPLY:
diff --git a/src/ipcpd/unicast/fa.c b/src/ipcpd/unicast/fa.c
index fbcbc6fa..c1cb065f 100644
--- a/src/ipcpd/unicast/fa.c
+++ b/src/ipcpd/unicast/fa.c
@@ -66,6 +66,7 @@ struct fa_msg {
uint32_t loss;
uint32_t ber;
uint32_t max_gap;
+ uint16_t cypher_s;
} __attribute__((packed));
struct cmd {
@@ -217,6 +218,7 @@ static void * fa_handle_packet(void * o)
qs.ber = ntoh32(msg->ber);
qs.in_order = msg->in_order;
qs.max_gap = ntoh32(msg->max_gap);
+ qs.cypher_s = ntoh16(msg->cypher_s);
fd = ipcp_flow_req_arr((uint8_t *) (msg + 1),
ipcp_dir_hash_len(),
@@ -386,6 +388,7 @@ int fa_alloc(int fd,
msg->ber = hton32(qs.ber);
msg->in_order = qs.in_order;
msg->max_gap = hton32(qs.max_gap);
+ msg->cypher_s = hton16(qs.cypher_s);
memcpy(msg + 1, dst, ipcp_dir_hash_len());
diff --git a/src/lib/config.h.in b/src/lib/config.h.in
index 3e5a7b1e..70261cab 100644
--- a/src/lib/config.h.in
+++ b/src/lib/config.h.in
@@ -24,6 +24,10 @@
#cmakedefine HAVE_LIBGCRYPT
#cmakedefine HAVE_OPENSSL
+#ifdef HAVE_OPENSSL
+#define HAVE_ENCRYPTION
+#endif
+
#define SYS_MAX_FLOWS @SYS_MAX_FLOWS@
#cmakedefine SHM_RBUFF_LOCKLESS
diff --git a/src/lib/crypt.c b/src/lib/crypt.c
new file mode 100644
index 00000000..94f1b50e
--- /dev/null
+++ b/src/lib/crypt.c
@@ -0,0 +1,490 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2019
+ *
+ * Elliptic curve Diffie-Hellman key exchange and
+ * AES encryption for flows using OpenSSL
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifdef HAVE_OPENSSL
+
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/pem.h>
+
+#define MSGBUFSZ 2048
+#define IVSZ 16
+#define DH_TIMEO 2 /* seconds */
+/* SYMMKEYSZ defined in dev.c */
+
+/*
+ * Derive the common secret from
+ * your public key pair (kp)
+ * the remote public key (pub).
+ * Store it in a preallocated buffer (s).
+ */
+static int __openssl_ecdh_derive_secret(EVP_PKEY * kp,
+ EVP_PKEY * pub,
+ uint8_t * s)
+{
+ EVP_PKEY_CTX * ctx;
+ int ret;
+ uint8_t * secret;
+ size_t secret_len;
+
+ ctx = EVP_PKEY_CTX_new(kp, NULL);
+ if (ctx == NULL)
+ goto fail_new;
+
+ ret = EVP_PKEY_derive_init(ctx);
+ if (ret != 1)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_derive_set_peer(ctx, pub);
+ if (ret != 1)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_derive(ctx, NULL, &secret_len);
+ if (ret != 1)
+ goto fail_ctx;
+
+ if (secret_len < SYMMKEYSZ)
+ goto fail_ctx;
+
+ secret = OPENSSL_malloc(secret_len);
+ if (secret == NULL)
+ goto fail_ctx;
+
+ ret = EVP_PKEY_derive(ctx, secret, &secret_len);
+ if (ret != 1)
+ goto fail_derive;
+
+ /* Hash the secret for use as AES key. */
+ mem_hash(HASH_SHA3_256, s, secret, secret_len);
+
+ OPENSSL_free(secret);
+ EVP_PKEY_CTX_free(ctx);
+
+ return 0;
+
+ fail_derive:
+ OPENSSL_free(s);
+ fail_ctx:
+ EVP_PKEY_CTX_free(ctx);
+ fail_new:
+ return -ECRYPT;
+}
+
+static int __openssl_ecdh_gen_key(EVP_PKEY ** kp)
+{
+ EVP_PKEY_CTX * ctx = NULL;
+ EVP_PKEY_CTX * kctx = NULL;
+ EVP_PKEY * params = NULL;
+ int ret;
+
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+ if (ctx == NULL)
+ goto fail_new_id;
+
+ ret = EVP_PKEY_paramgen_init(ctx);
+ if (ret != 1)
+ goto fail_paramgen;
+
+ ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_X9_62_prime256v1);
+ if (ret != 1)
+ goto fail_paramgen;
+
+ ret = EVP_PKEY_paramgen(ctx, &params);
+ if (ret != 1)
+ goto fail_paramgen;
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (kctx == NULL)
+ goto fail_keygen_init;
+
+ ret = EVP_PKEY_keygen_init(kctx);
+ if (ret != 1)
+ goto fail_keygen;
+
+ ret = EVP_PKEY_keygen(kctx, kp);
+ if (ret != 1)
+ goto fail_keygen;
+
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ EVP_PKEY_CTX_free(ctx);
+
+ return 0;
+
+ fail_keygen:
+ EVP_PKEY_CTX_free(kctx);
+ fail_keygen_init:
+ EVP_PKEY_free(params);
+ fail_paramgen:
+ EVP_PKEY_CTX_free(ctx);
+ fail_new_id:
+ return -ECRYPT;
+}
+
+/* ECDH from the server side. */
+static int openssl_ecdh_srv(int fd,
+ uint8_t * s)
+{
+ EVP_PKEY * kp = NULL;
+ EVP_PKEY * pub = NULL;
+ uint8_t buf[MSGBUFSZ];
+ ssize_t len;
+ int buf_sz;
+ uint8_t * pos;
+ struct timespec timeo = {DH_TIMEO,0};
+
+ assert(s != NULL);
+
+ (void) fd;
+ (void) s;
+
+ if (__openssl_ecdh_gen_key(&kp) < 0)
+ goto fail_gen_key;
+
+ fccntl(fd, FLOWSRCVTIMEO, &timeo);
+
+ len = flow_read(fd, buf, MSGBUFSZ);
+ if (len < 0) {
+ fccntl(fd, FLOWSRCVTIMEO, NULL);
+ goto fail_get_key;
+ }
+
+ fccntl(fd, FLOWSRCVTIMEO, NULL);
+
+ pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */
+ pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) len);
+
+ pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */
+ buf_sz = i2d_PUBKEY(kp, &pos);
+ if (buf_sz < 0)
+ goto fail_get_key;
+
+ if (flow_write(fd, buf, (size_t) buf_sz) < 0)
+ goto fail_get_key;
+
+ if (__openssl_ecdh_derive_secret(kp, pub, s) < 0)
+ goto fail_get_key;
+
+ EVP_PKEY_free(kp);
+ EVP_PKEY_free(pub);
+
+ return 0;
+
+ fail_get_key:
+ EVP_PKEY_free(kp);
+ fail_gen_key:
+ return -ECRYPT;
+}
+
+/* ECDH from the client side. */
+static int openssl_ecdh_clt(int fd,
+ uint8_t * s)
+{
+ EVP_PKEY * kp = NULL;
+ EVP_PKEY * pub = NULL;
+ uint8_t buf[MSGBUFSZ];
+ int buf_sz;
+ uint8_t * pos;
+ ssize_t len;
+ struct timespec timeo = {DH_TIMEO,0};
+
+ assert(s != NULL);
+
+ (void) fd;
+ (void) s;
+
+ if (__openssl_ecdh_gen_key(&kp) < 0)
+ goto fail_gen_key;
+
+ pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */
+ buf_sz = i2d_PUBKEY(kp, &pos);
+ if (buf_sz < 0)
+ goto fail_get_key;
+
+ if (flow_write(fd, buf, (size_t) buf_sz) < 0)
+ goto fail_get_key;
+
+ fccntl(fd, FLOWSRCVTIMEO, &timeo);
+
+ len = flow_read(fd, buf, MSGBUFSZ);
+ if (len < 0) {
+ fccntl(fd, FLOWSRCVTIMEO, NULL);
+ goto fail_get_key;
+ }
+
+ fccntl(fd, FLOWSRCVTIMEO, NULL);
+
+ pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */
+ pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) len);
+
+ if (__openssl_ecdh_derive_secret(kp, pub, s) < 0)
+ goto fail_get_key;
+
+ EVP_PKEY_free(kp);
+ EVP_PKEY_free(pub);
+
+ return 0;
+
+ fail_get_key:
+ EVP_PKEY_free(kp);
+ fail_gen_key:
+ return -ECRYPT;
+}
+
+/*
+ * AES encryption calls. If FRCT is disabled, we should generate a
+ * 128-bit random IV and append it to the packet. If the flow is
+ * reliable, we could initialize the context once, and consider the
+ * stream a single encrypted message to avoid initializing the
+ * encryption context for each packet.
+ */
+
+static int openssl_encrypt(struct flow * f,
+ struct shm_du_buff * sdb)
+{
+ uint8_t * out;
+ uint8_t * in;
+ uint8_t * head;
+ uint8_t iv[IVSZ];
+ int in_sz;
+ int out_sz;
+ int tmp_sz;
+ int ret;
+
+ in = shm_du_buff_head(sdb);
+ in_sz = shm_du_buff_tail(sdb) - in;
+
+ if (random_buffer(iv, IVSZ) < 0)
+ goto fail_iv;
+
+ out = malloc(in_sz + EVP_MAX_BLOCK_LENGTH);
+ if (out == NULL)
+ goto fail_iv;
+
+ EVP_CIPHER_CTX_reset(f->ctx);
+
+ ret = EVP_EncryptInit_ex(f->ctx,
+ EVP_aes_256_cbc(),
+ NULL,
+ f->key,
+ iv);
+ if (ret != 1)
+ goto fail_encrypt_init;
+
+ ret = EVP_EncryptUpdate(f->ctx, out, &tmp_sz, in, in_sz);
+ if (ret != 1)
+ goto fail_encrypt;
+
+ out_sz = tmp_sz;
+ ret = EVP_EncryptFinal_ex(f->ctx, out + tmp_sz, &tmp_sz);
+ if (ret != 1)
+ goto fail_encrypt;
+
+ out_sz += tmp_sz;
+
+ EVP_CIPHER_CTX_cleanup(f->ctx);
+
+ assert(out_sz >= in_sz);
+
+ head = shm_du_buff_head_alloc(sdb, IVSZ);
+ if (head == NULL)
+ goto fail_encrypt;
+
+ if (shm_du_buff_tail_alloc(sdb, out_sz - in_sz) == NULL)
+ goto fail_tail_alloc;
+
+ memcpy(head, iv, IVSZ);
+ memcpy(in, out, out_sz);
+
+ free(out);
+
+ return 0;
+
+ fail_tail_alloc:
+ shm_du_buff_head_release(sdb, IVSZ);
+ fail_encrypt:
+ EVP_CIPHER_CTX_cleanup(f->ctx);
+ fail_encrypt_init:
+ free(out);
+ fail_iv:
+ return -ECRYPT;
+}
+
+static int openssl_decrypt(struct flow * f,
+ struct shm_du_buff * sdb)
+{
+ uint8_t * in;
+ uint8_t * out;
+ uint8_t iv[IVSZ];
+ int ret;
+ int out_sz;
+ int in_sz;
+ int tmp_sz;
+
+ in = shm_du_buff_head_release(sdb, IVSZ);
+
+ memcpy(iv, in, IVSZ);
+
+ in = shm_du_buff_head(sdb);
+
+ in_sz = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb);
+
+ out = malloc(in_sz);
+ if (out == NULL)
+ goto fail_malloc;
+
+ EVP_CIPHER_CTX_reset(f->ctx);
+
+ ret = EVP_DecryptInit_ex(f->ctx,
+ EVP_aes_256_cbc(),
+ NULL,
+ f->key,
+ iv);
+ if (ret != 1)
+ goto fail_decrypt_init;
+
+ ret = EVP_DecryptUpdate(f->ctx, out, &tmp_sz, in, in_sz);
+ if (ret != 1)
+ goto fail_decrypt;
+
+ out_sz = tmp_sz;
+
+ ret = EVP_DecryptFinal_ex(f->ctx, out + tmp_sz, &tmp_sz);
+ if (ret != 1)
+ goto fail_decrypt;
+
+ out_sz += tmp_sz;
+
+ assert(out_sz <= in_sz);
+
+ shm_du_buff_tail_release(sdb, in_sz - out_sz);
+
+ memcpy(in, out, out_sz);
+
+ free(out);
+
+ return 0;
+
+ fail_decrypt:
+ EVP_CIPHER_CTX_cleanup(f->ctx);
+ fail_decrypt_init:
+ free(out);
+ fail_malloc:
+ return -ECRYPT;
+
+}
+
+static int openssl_crypt_init(struct flow * f)
+{
+ f->ctx = EVP_CIPHER_CTX_new();
+ if (f->ctx == NULL)
+ goto fail_new;
+
+ return 0;
+
+ fail_new:
+ return -ECRYPT;
+}
+
+static void openssl_crypt_fini(struct flow * f)
+{
+ EVP_CIPHER_CTX_free(f->ctx);
+ f->ctx = NULL;
+}
+
+#endif /* HAVE_OPENSSL */
+
+static int crypt_dh_srv(int fd,
+ uint8_t * s)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_ecdh_clt(fd, s);
+#else
+ (void) fd;
+
+ memset(s, 0, SYMMKEYSZ);
+
+ return -ECRYPT;
+#endif
+}
+
+static int crypt_dh_clt(int fd,
+ uint8_t * s)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_ecdh_srv(fd, s);
+#else
+ (void) fd;
+
+ memset(s, 0, SYMMKEYSZ);
+
+ return 0;
+#endif
+}
+
+static int crypt_encrypt(struct flow * f,
+ struct shm_du_buff * sdb)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_encrypt(f, sdb);
+#else
+ (void) f;
+ (void) sdb;
+
+ return 0;
+#endif
+}
+
+static int crypt_decrypt(struct flow * f,
+ struct shm_du_buff * sdb)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_decrypt(f, sdb);
+#else
+ (void) f;
+ (void) sdb;
+
+ return 0;
+#endif
+}
+
+
+static int crypt_init(struct flow * f)
+{
+#ifdef HAVE_OPENSSL
+ return openssl_crypt_init(f);
+#else
+ (void) f;
+
+ return 0;
+#endif
+}
+
+static void crypt_fini(struct flow * f)
+{
+#ifdef HAVE_OPENSSL
+ openssl_crypt_fini(f);
+#else
+ (void) f;
+#endif
+}
diff --git a/src/lib/dev.c b/src/lib/dev.c
index 229a1470..1e0a177d 100644
--- a/src/lib/dev.c
+++ b/src/lib/dev.c
@@ -29,6 +29,7 @@
#include "config.h"
#include <ouroboros/hash.h>
+#include <ouroboros/cacep.h>
#include <ouroboros/errno.h>
#include <ouroboros/dev.h>
#include <ouroboros/ipcp-dev.h>
@@ -59,6 +60,8 @@
#define DONE_PART -2
#define CRCLEN (sizeof(uint32_t))
+#define SECMEMSZ 16384
+#define SYMMKEYSZ 32
struct flow_set {
size_t idx;
@@ -98,6 +101,9 @@ struct flow {
qosspec_t spec;
ssize_t part_idx;
+ void * ctx;
+ uint8_t key[SYMMKEYSZ];
+
pid_t pid;
bool snd_timesout;
@@ -237,6 +243,8 @@ static void flow_clear(int fd)
ai.flows[fd].pid = -1;
}
+#include "crypt.c"
+
static void flow_fini(int fd)
{
assert(fd >= 0 && fd < SYS_MAX_FLOWS);
@@ -266,6 +274,9 @@ static void flow_fini(int fd)
if (ai.flows[fd].frcti != NULL)
frcti_destroy(ai.flows[fd].frcti);
+ if (ai.flows[fd].spec.cypher_s > 0)
+ crypt_fini(&ai.flows[fd]);
+
flow_clear(fd);
}
@@ -286,15 +297,15 @@ static int flow_init(int flow_id,
ai.flows[fd].rx_rb = shm_rbuff_open(ai.pid, flow_id);
if (ai.flows[fd].rx_rb == NULL)
- goto fail;
+ goto fail_rx_rb;
ai.flows[fd].tx_rb = shm_rbuff_open(pid, flow_id);
if (ai.flows[fd].tx_rb == NULL)
- goto fail;
+ goto fail_tx_rb;
ai.flows[fd].set = shm_flow_set_open(pid);
if (ai.flows[fd].set == NULL)
- goto fail;
+ goto fail_set;
ai.flows[fd].flow_id = flow_id;
ai.flows[fd].oflags = FLOWFDEFAULT;
@@ -302,6 +313,9 @@ static int flow_init(int flow_id,
ai.flows[fd].part_idx = NO_PART;
ai.flows[fd].spec = qs;
+ if (qs.cypher_s > 0 && crypt_init(&ai.flows[fd]) < 0)
+ goto fail_crypt;
+
ai.ports[flow_id].fd = fd;
port_set_state(&ai.ports[flow_id], PORT_ID_ASSIGNED);
@@ -310,8 +324,14 @@ static int flow_init(int flow_id,
return fd;
- fail:
- flow_fini(fd);
+ fail_crypt:
+ shm_flow_set_close(ai.flows[fd].set);
+ fail_set:
+ shm_rbuff_close(ai.flows[fd].tx_rb);
+ fail_tx_rb:
+ shm_rbuff_close(ai.flows[fd].rx_rb);
+ fail_rx_rb:
+ bmp_release(ai.fds, fd);
fail_fds:
pthread_rwlock_unlock(&ai.lock);
return err;
@@ -344,12 +364,11 @@ static void init(int argc,
ai.pid = getpid();
#ifdef HAVE_LIBGCRYPT
- if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
+ if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
if (!gcry_check_version(GCRYPT_VERSION))
goto fail_fds;
- /* Needs to be enabled when we add encryption. */
- gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
- gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+ gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
#endif
ai.fds = bmp_create(PROG_MAX_FLOWS - PROG_RES_FDS, PROG_RES_FDS);
@@ -550,8 +569,8 @@ int flow_accept(qosspec_t * qs,
if (ai.flows[fd].spec.in_order != 0) {
ai.flows[fd].frcti = frcti_create(fd);
if (ai.flows[fd].frcti == NULL) {
- flow_fini(fd);
pthread_rwlock_unlock(&ai.lock);
+ flow_dealloc(fd);
return -ENOMEM;
}
}
@@ -561,6 +580,31 @@ int flow_accept(qosspec_t * qs,
pthread_rwlock_unlock(&ai.lock);
+ /* TODO: piggyback public keys at flow allocation. */
+ if (ai.flows[fd].spec.cypher_s > 0) {
+ uint8_t key[SYMMKEYSZ];
+ uint16_t tmp;
+
+ pthread_rwlock_wrlock(&ai.lock);
+
+ tmp = ai.flows[fd].spec.cypher_s;
+ ai.flows[fd].spec.cypher_s = 0;
+
+ pthread_rwlock_unlock(&ai.lock);
+
+ if (crypt_dh_srv(fd, key) < 0) {
+ flow_dealloc(fd);
+ return -ECRYPT;
+ }
+
+ pthread_rwlock_wrlock(&ai.lock);
+
+ ai.flows[fd].spec.cypher_s = tmp;
+ memcpy(ai.flows[fd].key, key, SYMMKEYSZ);
+
+ pthread_rwlock_unlock(&ai.lock);
+ }
+
return fd;
}
@@ -579,9 +623,9 @@ static int __flow_alloc(const char * dst,
qs->ber = 1;
#endif
if (join)
- msg.code = IRM_MSG_CODE__IRM_FLOW_JOIN;
+ msg.code = IRM_MSG_CODE__IRM_FLOW_JOIN;
else
- msg.code = IRM_MSG_CODE__IRM_FLOW_ALLOC;
+ msg.code = IRM_MSG_CODE__IRM_FLOW_ALLOC;
msg.dst = (char *) dst;
msg.has_pid = true;
msg.pid = ai.pid;
@@ -605,7 +649,7 @@ static int __flow_alloc(const char * dst,
}
if (recv_msg->result != 0) {
- int res = recv_msg->result;
+ int res = recv_msg->result;
irm_msg__free_unpacked(recv_msg, NULL);
return res;
}
@@ -630,14 +674,39 @@ static int __flow_alloc(const char * dst,
if (ai.flows[fd].spec.in_order != 0) {
ai.flows[fd].frcti = frcti_create(fd);
if (ai.flows[fd].frcti == NULL) {
- flow_fini(fd);
pthread_rwlock_unlock(&ai.lock);
+ flow_dealloc(fd);
return -ENOMEM;
}
}
pthread_rwlock_unlock(&ai.lock);
+ /* TODO: piggyback public keys at flow allocation. */
+ if (!join && ai.flows[fd].spec.cypher_s > 0) {
+ uint8_t key[SYMMKEYSZ];
+ uint16_t tmp;
+
+ pthread_rwlock_wrlock(&ai.lock);
+
+ tmp = ai.flows[fd].spec.cypher_s;
+ ai.flows[fd].spec.cypher_s = 0;
+
+ pthread_rwlock_unlock(&ai.lock);
+
+ if (crypt_dh_clt(fd, key) < 0) {
+ flow_dealloc(fd);
+ return -ECRYPT;
+ }
+
+ pthread_rwlock_wrlock(&ai.lock);
+
+ ai.flows[fd].spec.cypher_s = tmp;
+ memcpy(ai.flows[fd].key, key, SYMMKEYSZ);
+
+ pthread_rwlock_unlock(&ai.lock);
+ }
+
return fd;
}
@@ -931,6 +1000,15 @@ ssize_t flow_write(int fd,
return -ENOMEM;
}
+ pthread_rwlock_wrlock(&ai.lock);
+ if (flow->spec.cypher_s > 0)
+ if (crypt_encrypt(flow, sdb) < 0) {
+ pthread_rwlock_unlock(&ai.lock);
+ shm_rdrbuff_remove(ai.rdrb, idx);
+ return -ENOMEM;
+ }
+ pthread_rwlock_unlock(&ai.lock);
+
if (flow->spec.ber == 0 && add_crc(sdb) != 0) {
shm_rdrbuff_remove(ai.rdrb, idx);
return -ENOMEM;
@@ -1009,9 +1087,22 @@ ssize_t flow_read(int fd,
shm_rbuff_read_b(rb, abstime);
if (idx < 0)
return idx;
+
sdb = shm_rdrbuff_get(ai.rdrb, idx);
- if (flow->spec.ber == 0 && chk_crc(sdb) != 0)
+ if (flow->spec.ber == 0 && chk_crc(sdb) != 0) {
+ shm_rdrbuff_remove(ai.rdrb, idx);
continue;
+ }
+
+ pthread_rwlock_wrlock(&ai.lock);
+ if (flow->spec.cypher_s > 0)
+ if (crypt_decrypt(flow, sdb) < 0) {
+ pthread_rwlock_unlock(&ai.lock);
+ shm_rdrbuff_remove(ai.rdrb,
+ idx);
+ return -ENOMEM;
+ }
+ pthread_rwlock_unlock(&ai.lock);
} while (frcti_rcv(flow->frcti, sdb) != 0);
}
}
diff --git a/src/lib/qosspec.proto b/src/lib/qosspec.proto
index c9e5a6e6..0cbcd41a 100644
--- a/src/lib/qosspec.proto
+++ b/src/lib/qosspec.proto
@@ -30,4 +30,5 @@ message qosspec_msg {
required uint32 ber = 5; /* Bit error rate, ppb */
required uint32 in_order = 6; /* In-order delivery */
required uint32 max_gap = 7; /* In ms */
+ required uint32 cypher_s = 8; /* Crypto strength in bits */
};
diff --git a/src/lib/sockets.c b/src/lib/sockets.c
index b08bae8e..347e9244 100644
--- a/src/lib/sockets.c
+++ b/src/lib/sockets.c
@@ -166,7 +166,7 @@ char * ipcp_sock_path(pid_t pid)
return full_name;
}
-qosspec_msg_t spec_to_msg(qosspec_t * qs)
+qosspec_msg_t spec_to_msg(const qosspec_t * qs)
{
qosspec_t spec;
qosspec_msg_t msg = QOSSPEC_MSG__INIT;
@@ -180,11 +180,12 @@ qosspec_msg_t spec_to_msg(qosspec_t * qs)
msg.ber = spec.ber;
msg.in_order = spec.in_order;
msg.max_gap = spec.max_gap;
+ msg.cypher_s = spec.cypher_s;
return msg;
}
-qosspec_t msg_to_spec(qosspec_msg_t * msg)
+qosspec_t msg_to_spec(const qosspec_msg_t * msg)
{
qosspec_t spec;
@@ -197,6 +198,7 @@ qosspec_t msg_to_spec(qosspec_msg_t * msg)
spec.ber = msg->ber;
spec.in_order = msg->in_order;
spec.max_gap = msg->max_gap;
+ spec.cypher_s = msg->cypher_s;
return spec;
}