diff options
author | Matt Dunwoodie <ncon@noconroy.net> | 2021-04-19 05:45:47 +1000 |
---|---|---|
committer | Matt Dunwoodie <ncon@noconroy.net> | 2021-04-19 11:16:40 +1000 |
commit | f685f466dbc371783b6bc8920f542cae2bd54fd8 (patch) | |
tree | f63e231fe790c60641540e23f40fe1a601fafcc0 | |
parent | if_wg: actually use DEFAULT_MTU value (diff) | |
download | wireguard-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.c | 126 | ||||
-rw-r--r-- | src/crypto.h | 9 | ||||
-rw-r--r-- | src/if_wg.c | 25 | ||||
-rw-r--r-- | src/wg_noise.c | 15 | ||||
-rw-r--r-- | src/wg_noise.h | 8 |
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( |