aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Dunwoodie <ncon@noconroy.net>2021-04-19 05:45:47 +1000
committerMatt Dunwoodie <ncon@noconroy.net>2021-04-19 11:16:40 +1000
commitf685f466dbc371783b6bc8920f542cae2bd54fd8 (patch)
treef63e231fe790c60641540e23f40fe1a601fafcc0
parentif_wg: actually use DEFAULT_MTU value (diff)
downloadwireguard-freebsd-f685f466dbc371783b6bc8920f542cae2bd54fd8.tar.xz
wireguard-freebsd-f685f466dbc371783b6bc8920f542cae2bd54fd8.zip
crypto: encrypt mbuf in place
This introduces a couple of routines to encrypt the mbufs in place. It is likely that these will be replaced by something in opencrypto, however for the time being this fixes a heap overflow and sets up wg_noise for the "correct" API. When the time comes, this should make it easier to drop in new crypto. It should be noted, this was written at 0500. Signed-off-by: Matt Dunwoodie <ncon@noconroy.net>
-rw-r--r--src/crypto.c126
-rw-r--r--src/crypto.h9
-rw-r--r--src/if_wg.c25
-rw-r--r--src/wg_noise.c15
-rw-r--r--src/wg_noise.h8
5 files changed, 150 insertions, 33 deletions
diff --git a/src/crypto.c b/src/crypto.c
index 97bef62..88a7d6d 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -200,6 +200,49 @@ static void chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in,
}
}
+static void chacha20_mbuf(struct chacha20_ctx *ctx, struct mbuf *m0)
+{
+ uint32_t buf[CHACHA20_BLOCK_WORDS];
+ uint8_t *txt, *blk;
+ struct mbuf *m;
+ int len, leftover = 0;
+
+ for (m = m0; m; m = m->m_next) {
+ len = m->m_len;
+ txt = m->m_data;
+ while (len) {
+ if (leftover == 0) {
+ chacha20_block(ctx, buf);
+ blk = (uint8_t *)buf;
+ if (len >= CHACHA20_BLOCK_SIZE) {
+ xor_cpy(txt, txt, blk, CHACHA20_BLOCK_SIZE);
+ len -= CHACHA20_BLOCK_SIZE;
+ txt += CHACHA20_BLOCK_SIZE;
+ } else {
+ xor_cpy(txt, txt, blk, len);
+ leftover = CHACHA20_BLOCK_SIZE - len;
+ blk += len;
+ len = 0;
+ }
+ } else {
+ if (len >= leftover) {
+ xor_cpy(txt, txt, blk, leftover);
+ len -= leftover;
+ txt += leftover;
+ blk += leftover;
+ leftover = 0;
+ } else {
+ xor_cpy(txt, txt, blk, len);
+ leftover -= len;
+ txt += len;
+ blk += len;
+ len = 0;
+ }
+ }
+ }
+ }
+}
+
static void hchacha20(uint32_t derived_key[CHACHA20_KEY_WORDS],
const uint8_t nonce[HCHACHA20_NONCE_SIZE],
const uint8_t key[HCHACHA20_KEY_SIZE])
@@ -587,6 +630,89 @@ chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
return ret;
}
+bool
+chacha20poly1305_encrypt_mbuf(struct mbuf *m0, const uint64_t nonce,
+ const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
+{
+ struct poly1305_ctx poly1305_state;
+ struct chacha20_ctx chacha20_state;
+ struct mbuf *m;
+ bool ret;
+ union {
+ uint8_t block0[POLY1305_KEY_SIZE];
+ uint8_t mac[POLY1305_MAC_SIZE];
+ uint64_t lens[2];
+ } b = { { 0 } };
+
+ chacha20_init(&chacha20_state, key, nonce);
+ chacha20(&chacha20_state, b.block0, b.block0, sizeof(b.block0));
+ poly1305_init(&poly1305_state, b.block0);
+
+ chacha20_mbuf(&chacha20_state, m0);
+ for (m = m0; m; m = m->m_next)
+ poly1305_update(&poly1305_state, m->m_data, m->m_len);
+ poly1305_update(&poly1305_state, pad0, (0x10 - m0->m_pkthdr.len) & 0xf);
+
+ b.lens[0] = 0;
+ b.lens[1] = cpu_to_le64(m0->m_pkthdr.len);
+ poly1305_update(&poly1305_state, (uint8_t *)b.lens, sizeof(b.lens));
+
+ poly1305_final(&poly1305_state, b.mac);
+ ret = m_append(m0, POLY1305_MAC_SIZE, b.mac);
+
+ explicit_bzero(&chacha20_state, sizeof(chacha20_state));
+ explicit_bzero(&b, sizeof(b));
+ return ret;
+}
+
+bool
+chacha20poly1305_decrypt_mbuf(struct mbuf *m0, const uint64_t nonce,
+ const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
+{
+ struct poly1305_ctx poly1305_state;
+ struct chacha20_ctx chacha20_state;
+ uint8_t mbuf_mac[POLY1305_MAC_SIZE];
+ struct mbuf *m;
+ bool ret;
+ size_t dst_len;
+ union {
+ uint8_t block0[POLY1305_KEY_SIZE];
+ uint8_t mac[POLY1305_MAC_SIZE];
+ uint64_t lens[2];
+ } b = { { 0 } };
+
+ if (m0->m_pkthdr.len < POLY1305_MAC_SIZE)
+ return false;
+
+ chacha20_init(&chacha20_state, key, nonce);
+ chacha20(&chacha20_state, b.block0, b.block0, sizeof(b.block0));
+ poly1305_init(&poly1305_state, b.block0);
+
+ dst_len = m0->m_pkthdr.len - POLY1305_MAC_SIZE;
+ m_copydata(m0, dst_len, POLY1305_MAC_SIZE, mbuf_mac);
+ m_adj(m0, -POLY1305_MAC_SIZE);
+
+ for (m = m0; m; m = m->m_next)
+ poly1305_update(&poly1305_state, m->m_data, m->m_len);
+
+ poly1305_update(&poly1305_state, pad0, (0x10 - m0->m_pkthdr.len) & 0xf);
+
+ b.lens[0] = 0;
+ b.lens[1] = cpu_to_le64(dst_len);
+ poly1305_update(&poly1305_state, (uint8_t *)b.lens, sizeof(b.lens));
+
+ poly1305_final(&poly1305_state, b.mac);
+
+ ret = timingsafe_bcmp(b.mac, mbuf_mac, POLY1305_MAC_SIZE) == 0;
+ if (ret)
+ chacha20_mbuf(&chacha20_state, m0);
+
+ explicit_bzero(&chacha20_state, sizeof(chacha20_state));
+ explicit_bzero(&b, sizeof(b));
+
+ return ret;
+}
+
void
xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src,
const size_t src_len, const uint8_t *ad,
diff --git a/src/crypto.h b/src/crypto.h
index 0ac23f9..b1a5f0e 100644
--- a/src/crypto.h
+++ b/src/crypto.h
@@ -7,6 +7,7 @@
#define _WG_CRYPTO
#include <sys/types.h>
+#include <sys/mbuf.h>
enum chacha20poly1305_lengths {
XCHACHA20POLY1305_NONCE_SIZE = 24,
@@ -26,6 +27,14 @@ chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, const size_t src_len,
const uint64_t nonce,
const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
+bool
+chacha20poly1305_encrypt_mbuf(struct mbuf *, const uint64_t nonce,
+ const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
+
+bool
+chacha20poly1305_decrypt_mbuf(struct mbuf *, const uint64_t nonce,
+ const uint8_t key[CHACHA20POLY1305_KEY_SIZE]);
+
void
xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src,
const size_t src_len, const uint8_t *ad,
diff --git a/src/if_wg.c b/src/if_wg.c
index 9719ead..ea9c00d 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -1423,7 +1423,7 @@ wg_encrypt(struct wg_softc *sc, struct wg_packet *pkt)
struct mbuf *m;
uint32_t idx;
uint8_t zeroed[NOISE_AUTHTAG_LEN] = { 0 };
- int pad, len;
+ int pad;
peer = noise_keypair_remote_arg(pkt->p_keypair);
m = pkt->p_mbuf;
@@ -1439,17 +1439,9 @@ wg_encrypt(struct wg_softc *sc, struct wg_packet *pkt)
if (pad != 0 && !m_append(m, pad, zeroed))
goto error;
- /* TODO teach noise_keypair_encrypt about mbufs. Currently we have to
- * resort to m_defrag to create an encryptable buffer. */
- len = m->m_pkthdr.len;
- if (!m_append(m, NOISE_AUTHTAG_LEN, zeroed))
- goto error;
- /* TODO this is buffer overflow territory */
- if ((m = m_defrag(m, M_NOWAIT)) == NULL)
- goto error;
-
/* Do encryption */
- noise_keypair_encrypt(pkt->p_keypair, &idx, pkt->p_nonce, mtod(m, uint8_t *), len);
+ if (noise_keypair_encrypt(pkt->p_keypair, &idx, pkt->p_nonce, m) != 0)
+ goto error;
/* Put header into packet */
M_PREPEND(m, sizeof(struct wg_pkt_data), M_NOWAIT);
@@ -1490,15 +1482,8 @@ wg_decrypt(struct wg_softc *sc, struct wg_packet *pkt)
memcpy(&data, mtod(m, void *), sizeof(struct wg_pkt_data));
m_adj(m, sizeof(struct wg_pkt_data));
- /* TODO teach noise_keypair_decrypt about mbufs. Currently we have to
- * resort to m_defrag to create an decryptable buffer. */
- /* TODO this is buffer overflow territory */
- if ((m = m_defrag(m, M_NOWAIT)) == NULL) {
- goto error;
- }
-
pkt->p_nonce = le64toh(data.nonce);
- res = noise_keypair_decrypt(pkt->p_keypair, pkt->p_nonce, mtod(m, void *), m->m_pkthdr.len);
+ res = noise_keypair_decrypt(pkt->p_keypair, pkt->p_nonce, m);
if (__predict_false(res == EINVAL)) {
goto error;
@@ -1508,8 +1493,6 @@ wg_decrypt(struct wg_softc *sc, struct wg_packet *pkt)
panic("unexpected response: %d\n", res);
}
- m_adj(m, -NOISE_AUTHTAG_LEN);
-
/* A packet with length 0 is a keepalive packet */
if (__predict_false(m->m_pkthdr.len == 0)) {
DPRINTF(sc, "Receiving keepalive packet from peer "
diff --git a/src/wg_noise.c b/src/wg_noise.c
index 5d92750..7cf160b 100644
--- a/src/wg_noise.c
+++ b/src/wg_noise.c
@@ -859,23 +859,24 @@ noise_keep_key_fresh_recv(struct noise_remote *r)
return (keep_key_fresh ? ESTALE : 0);
}
-void
-noise_keypair_encrypt(struct noise_keypair *kp, uint32_t *r_idx, uint64_t nonce,
- uint8_t *buf, size_t buflen)
+int
+noise_keypair_encrypt(struct noise_keypair *kp, uint32_t *r_idx, uint64_t nonce, struct mbuf *m)
{
- chacha20poly1305_encrypt(buf, buf, buflen, NULL, 0, nonce, kp->kp_send);
+ if (chacha20poly1305_encrypt_mbuf(m, nonce, kp->kp_send) == 0)
+ return (ENOMEM);
+
*r_idx = kp->kp_index.i_remote_index;
+ return (0);
}
int
-noise_keypair_decrypt(struct noise_keypair *kp, uint64_t nonce, uint8_t *buf,
- size_t buflen)
+noise_keypair_decrypt(struct noise_keypair *kp, uint64_t nonce, struct mbuf *m)
{
if (READ_ONCE(kp->kp_nonce_recv) >= REJECT_AFTER_MESSAGES ||
noise_timer_expired(&kp->kp_birthdate, REJECT_AFTER_TIME, 0))
return (EINVAL);
- if (chacha20poly1305_decrypt(buf, buf, buflen, NULL, 0, nonce, kp->kp_recv) == 0)
+ if (chacha20poly1305_decrypt_mbuf(m, nonce, kp->kp_recv) == 0)
return (EINVAL);
if (noise_received_with(kp) != 0)
diff --git a/src/wg_noise.h b/src/wg_noise.h
index 50890a3..a6de34b 100644
--- a/src/wg_noise.h
+++ b/src/wg_noise.h
@@ -86,17 +86,15 @@ int noise_keypair_nonce_check(struct noise_keypair *, uint64_t);
int noise_keep_key_fresh_send(struct noise_remote *);
int noise_keep_key_fresh_recv(struct noise_remote *);
-void noise_keypair_encrypt(
+int noise_keypair_encrypt(
struct noise_keypair *,
uint32_t *r_idx,
uint64_t nonce,
- uint8_t *buf,
- size_t buflen);
+ struct mbuf *);
int noise_keypair_decrypt(
struct noise_keypair *,
uint64_t nonce,
- uint8_t *buf,
- size_t buflen);
+ struct mbuf *);
/* Handshake functions */
int noise_create_initiation(