diff --git a/sys/crypto/poly1305.c b/sys/crypto/poly1305.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2e0e719a88081f9cbcf8fb97f0e352a0723f2d8
--- /dev/null
+++ b/sys/crypto/poly1305.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 Andrew Moon (dedicated to the public domain)
+ * Copyright Koen Zandberg <koen@bergzand.net>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup sys_crypto_poly1305
+ * @{
+ * @file
+ * @brief   Implementation of Poly1305. Based on Floodberry's and Loup
+ *          Valliant's implementation. Optimized for small flash size.
+ *
+ * @author  Koen Zandberg <koen@bergzand.net>
+ * @}
+ */
+
+#include <string.h>
+#include "crypto/poly1305.h"
+
+static void poly1305_block(poly1305_ctx_t *ctx, uint8_t c4);
+
+static uint32_t u8to32(const uint8_t *p)
+{
+    return
+        ((uint32_t)p[0] |
+        ((uint32_t)p[1] <<  8) |
+        ((uint32_t)p[2] << 16) |
+        ((uint32_t)p[3] << 24));
+}
+
+static void u32to8(uint8_t *p, uint32_t v)
+{
+    p[0] = (uint8_t)(v);
+    p[1] = (uint8_t)(v >>  8);
+    p[2] = (uint8_t)(v >> 16);
+    p[3] = (uint8_t)(v >> 24);
+}
+
+static void _clear_c(poly1305_ctx_t *ctx)
+{
+    ctx->c[0]  = 0;
+    ctx->c[1]  = 0;
+    ctx->c[2]  = 0;
+    ctx->c[3]  = 0;
+    ctx->c_idx = 0;
+}
+
+static void poly1305_block(poly1305_ctx_t *ctx, uint8_t c4)
+{
+    /* Local copies */
+    const uint32_t r0 = ctx->r[0];
+    const uint32_t r1 = ctx->r[1];
+    const uint32_t r2 = ctx->r[2];
+    const uint32_t r3 = ctx->r[3];
+
+    const uint32_t rr0 = (r0 >> 2) * 5;
+    const uint32_t rr1 = (r1 >> 2) + r1;
+    const uint32_t rr2 = (r2 >> 2) + r2;
+    const uint32_t rr3 = (r3 >> 2) + r3;
+
+    /* s = h + c, without carry propagation */
+    const uint64_t s0 = ctx->h[0] + (uint64_t)ctx->c[0];
+    const uint64_t s1 = ctx->h[1] + (uint64_t)ctx->c[1];
+    const uint64_t s2 = ctx->h[2] + (uint64_t)ctx->c[2];
+    const uint64_t s3 = ctx->h[3] + (uint64_t)ctx->c[3];
+    const uint32_t s4 = ctx->h[4] + c4;
+
+    /* (h + c) * r, without carry propagation */
+    const uint64_t x0 = s0*r0 + s1*rr3 + s2*rr2 + s3*rr1 +s4*rr0;
+    const uint64_t x1 = s0*r1 + s1*r0  + s2*rr3 + s3*rr2 +s4*rr1;
+    const uint64_t x2 = s0*r2 + s1*r1  + s2*r0  + s3*rr3 +s4*rr2;
+    const uint64_t x3 = s0*r3 + s1*r2  + s2*r1  + s3*r0  +s4*rr3;
+    const uint32_t x4 = s4 * (r0 & 3);
+
+    /* partial reduction modulo 2^130 - 5 */
+    const uint32_t u5 = x4 + (x3 >> 32); // u5 <= 7ffffff5
+    const uint64_t u0 = (u5 >>  2) * 5 + (x0 & 0xffffffff);
+    const uint64_t u1 = (u0 >> 32)     + (x1 & 0xffffffff) + (x0 >> 32);
+    const uint64_t u2 = (u1 >> 32)     + (x2 & 0xffffffff) + (x1 >> 32);
+    const uint64_t u3 = (u2 >> 32)     + (x3 & 0xffffffff) + (x2 >> 32);
+    const uint64_t u4 = (u3 >> 32)     + (u5 & 3);
+
+    /* Update the hash */
+    ctx->h[0] = (uint32_t)u0;
+    ctx->h[1] = (uint32_t)u1;
+    ctx->h[2] = (uint32_t)u2;
+    ctx->h[3] = (uint32_t)u3;
+    ctx->h[4] = (uint32_t)u4;
+}
+
+static void _take_input(poly1305_ctx_t *ctx, uint8_t input)
+{
+    size_t word = ctx->c_idx >> 2;
+    size_t byte = ctx->c_idx & 3;
+    ctx->c[word] |= (uint32_t)input << (byte * 8);
+    ctx->c_idx++;
+}
+
+void poly1305_update(poly1305_ctx_t *ctx, const uint8_t *data, size_t len)
+{
+    for (size_t i = 0; i < len; i++) {
+        _take_input(ctx, data[i]);
+        if (ctx->c_idx == 16) {
+            poly1305_block(ctx, 1);
+            _clear_c(ctx);
+        }
+    }
+}
+
+void poly1305_init(poly1305_ctx_t *ctx, const uint8_t *key)
+{
+    /* load and clamp key */
+    ctx->r[0] = u8to32(key) & 0x0fffffff;
+    for (size_t i = 1; i < 4; i++) {
+        ctx->r[i] = u8to32(&key[4*i]) & 0x0ffffffc;
+    }
+    for (size_t i = 0; i < 4; i++) {
+        ctx->pad[i] = u8to32(&key[16 + i*4]);
+    }
+
+    /* Zero the hash */
+    memset(ctx->h, 0, sizeof(ctx->h));
+    _clear_c(ctx);
+}
+
+void poly1305_finish(poly1305_ctx_t *ctx, uint8_t *mac)
+{
+    /* Process the last block if there is data remaining */
+    if (ctx->c_idx) {
+        /* move the final 1 according to remaining input length */
+        /* (We may add less than 2^130 to the last input block) */
+        _take_input(ctx, 1);
+        /* And update hash */
+        poly1305_block(ctx, 0);
+    }
+
+    /* check if we should subtract 2^130-5 by performing the
+     * corresponding carry propagation. */
+    const uint64_t u0 = (uint64_t)5 + ctx->h[0]; // <= 1_00000004
+    const uint64_t u1 = (u0 >> 32)  + ctx->h[1]; // <= 1_00000000
+    const uint64_t u2 = (u1 >> 32)  + ctx->h[2]; // <= 1_00000000
+    const uint64_t u3 = (u2 >> 32)  + ctx->h[3]; // <= 1_00000000
+    const uint64_t u4 = (u3 >> 32)  + ctx->h[4]; // <=          5
+    /* u4 indicates how many times we should subtract 2^130-5 (0 or 1) */
+
+    /* h + pad, minus 2^130-5 if u4 exceeds 3 */
+    const uint64_t uu0 = (u4 >> 2) * 5 + ctx->h[0] + ctx->pad[0];
+    u32to8(mac, uu0);
+
+    const uint64_t uu1 = (uu0 >> 32)   + ctx->h[1] + ctx->pad[1];
+    u32to8(mac+4, uu1);
+
+    const uint64_t uu2 = (uu1 >> 32)   + ctx->h[2] + ctx->pad[2];
+    u32to8(mac+8, uu2);
+
+    const uint64_t uu3 = (uu2 >> 32)   + ctx->h[3] + ctx->pad[3];
+    u32to8(mac+12, uu3);
+
+}
+
+void poly1305_auth(uint8_t *mac, const uint8_t *data, size_t len, const uint8_t *key)
+{
+    poly1305_ctx_t ctx;
+
+    poly1305_init(&ctx, key);
+    poly1305_update(&ctx, data, len);
+    poly1305_finish(&ctx, mac);
+}
diff --git a/sys/include/crypto/poly1305.h b/sys/include/crypto/poly1305.h
new file mode 100644
index 0000000000000000000000000000000000000000..0512b0f40b23e8002b4eb8eb88fd4366baee544a
--- /dev/null
+++ b/sys/include/crypto/poly1305.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 Andrew Moon (dedicated to the public domain)
+ * Copyright (C) 2018 Freie Universität Berlin
+ * Copyright (C) 2018 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     sys_crypto
+ * @defgroup    sys_crypto_poly1305 poly1305
+ * @brief       Poly1305 one-time message authentication code
+ *
+ * Poly1305 is a one-time authenticator designed by D.J. Bernstein. It uses a
+ * 32-byte one-time key and a message and produces a 16-byte tag.
+ *
+ * @{
+ *
+ * @file
+ * @brief       Poly1305 MAC interface
+ *
+ * @author      Koen Zandberg <koen@bergzand.net>
+ *
+ * @see         https://tools.ietf.org/html/rfc8439#section-2.5
+ */
+#ifndef CRYPTO_POLY1305_H
+#define CRYPTO_POLY1305_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * @brief Poly1305 block size
+ */
+#define POLY1305_BLOCK_SIZE 16
+
+/**
+ * @brief Poly1305 context
+ */
+typedef struct {
+    uint32_t r[4];                          /**< first key part         */
+    uint32_t pad[4];                        /**< Second key part        */
+    uint32_t h[5];                          /**< Hash                   */
+    uint32_t c[4];                          /**< Message chunk          */
+    size_t c_idx;                           /**< Chunk length            */
+} poly1305_ctx_t;
+
+/**
+ * @brief   Initialize a poly1305 context
+ *
+ * @param   ctx     Poly1305 context
+ * @param   key     32 byte key
+ */
+void poly1305_init(poly1305_ctx_t *ctx, const uint8_t *key);
+
+/**
+ * @brief   Update the poly1305 context with a block of message
+ *
+ * @param   ctx     poly1305 context
+ * @param   data    ptr to the message
+ * @param   len     length of the message
+ */
+void poly1305_update(poly1305_ctx_t *ctx, const uint8_t *data, size_t len);
+
+/**
+ * @brief   Finish the poly1305 operation
+ *
+ * @param   ctx     poly1305 context
+ * @param   mac     16 byte buffer for the tag
+ */
+void poly1305_finish(poly1305_ctx_t *ctx, uint8_t *mac);
+
+/**
+ * @brief   Calculate a single poly1305 tag
+ *
+ * @param   mac     16 byte buffer for the tag
+ * @param   data    ptr to the message
+ * @param   len     length of the message
+ * @param   key     32 byte key
+ */
+void poly1305_auth(uint8_t *mac, const uint8_t *data, size_t len,
+                   const uint8_t *key);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* CRYPTO_POLY1305_H */
+/** @} */
diff --git a/tests/unittests/tests-crypto/tests-crypto-poly1305.c b/tests/unittests/tests-crypto/tests-crypto-poly1305.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ca5a29a4569351940600159e246f3a7cb03a480
--- /dev/null
+++ b/tests/unittests/tests-crypto/tests-crypto-poly1305.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ * Copyright (C) 2018 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+#include "embUnit/embUnit.h"
+#include "tests-crypto.h"
+
+#include "crypto/poly1305.h"
+
+#include <string.h>
+
+static const uint8_t key_1[32] = { 0 };
+static const uint8_t msg_1[64] = { 0 };
+static const uint8_t tag_1[16] = { 0 };
+
+static const uint8_t key_2[32] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70, 0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 0x86, 0x3e
+};
+
+static const uint8_t msg_2[] = {
+    0x41, 0x6e, 0x79, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+    0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e,
+    0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72,
+    0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69,
+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x72,
+    0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46,
+    0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x20,
+    0x6f, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73,
+    0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x77, 0x69,
+    0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+    0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x61, 0x63, 0x74, 0x69,
+    0x76, 0x69, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72,
+    0x65, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x22, 0x49, 0x45, 0x54, 0x46, 0x20, 0x43, 0x6f, 0x6e, 0x74,
+    0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2e, 0x20, 0x53, 0x75, 0x63, 0x68, 0x20,
+    0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75,
+    0x64, 0x65, 0x20, 0x6f, 0x72, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+    0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69,
+    0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x77, 0x65, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x20,
+    0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6c, 0x65, 0x63,
+    0x74, 0x72, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61,
+    0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e,
+    0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2c,
+    0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+    0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f,
+};
+
+static const uint8_t tag_2[] = {
+    0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70, 0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 0x86, 0x3e, 0x06,
+};
+
+static const uint8_t key_3[] = {
+    0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70, 0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 0x86, 0x3e,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t msg_3[] = {
+    0x41, 0x6e, 0x79, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+    0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e,
+    0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72,
+    0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69,
+    0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x72,
+    0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46,
+    0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x20,
+    0x6f, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73,
+    0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x77, 0x69,
+    0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+    0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x61, 0x63, 0x74, 0x69,
+    0x76, 0x69, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72,
+    0x65, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x22, 0x49, 0x45, 0x54, 0x46, 0x20, 0x43, 0x6f, 0x6e, 0x74,
+    0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2e, 0x20, 0x53, 0x75, 0x63, 0x68, 0x20,
+    0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75,
+    0x64, 0x65, 0x20, 0x6f, 0x72, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,
+    0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69,
+    0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x77, 0x65, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x20,
+    0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6c, 0x65, 0x63,
+    0x74, 0x72, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61,
+    0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e,
+    0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2c,
+    0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65,
+    0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f,
+};
+
+static const uint8_t tag_3[] = {
+    0xf3, 0x47, 0x7e, 0x7c, 0xd9, 0x54, 0x17, 0xaf, 0x89, 0xa6, 0xb8, 0x79, 0x4c, 0x31, 0x0c, 0xf0,
+};
+
+static const uint8_t key_4[] = {
+    0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
+    0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0,
+};
+
+static const uint8_t msg_4[] = {
+    0x27, 0x54, 0x77, 0x61, 0x73, 0x20, 0x62, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x67, 0x2c, 0x20, 0x61,
+    0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6c, 0x69, 0x74, 0x68, 0x79, 0x20, 0x74, 0x6f,
+    0x76, 0x65, 0x73, 0x0a, 0x44, 0x69, 0x64, 0x20, 0x67, 0x79, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64,
+    0x20, 0x67, 0x69, 0x6d, 0x62, 0x6c, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77,
+    0x61, 0x62, 0x65, 0x3a, 0x0a, 0x41, 0x6c, 0x6c, 0x20, 0x6d, 0x69, 0x6d, 0x73, 0x79, 0x20, 0x77,
+    0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6f, 0x72, 0x6f, 0x67, 0x6f, 0x76, 0x65,
+    0x73, 0x2c, 0x0a, 0x41, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x6d, 0x65, 0x20,
+    0x72, 0x61, 0x74, 0x68, 0x73, 0x20, 0x6f, 0x75, 0x74, 0x67, 0x72, 0x61, 0x62, 0x65, 0x2e,
+};
+
+static const uint8_t tag_4[] = {
+    0x45, 0x41, 0x66, 0x9a, 0x7e, 0xaa, 0xee, 0x61, 0xe7, 0x08, 0xdc, 0x7c, 0xbc, 0xc5, 0xeb, 0x62,
+};
+
+static const uint8_t key_5[] = {
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t msg_5[] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static const uint8_t tag_5[] = {
+    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t key_6[] = {
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static const uint8_t msg_6[] = {
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t tag_6[] = {
+    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t key_7[] = {
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t msg_7[] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t tag_7[] = {
+    0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t key_8[] = {
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t msg_8[] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFB, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+};
+
+static const uint8_t tag_8[] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t key_9[] = {
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t msg_9[] = {
+    0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static const uint8_t tag_9[] = {
+    0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static const uint8_t key_10[] = {
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t msg_10[] = {
+    0xE3, 0x35, 0x94, 0xD7, 0x50, 0x5E, 0x43, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x33, 0x94, 0xD7, 0x50, 0x5E, 0x43, 0x79, 0xCD, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t tag_10[] = {
+    0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t key_11[] = {
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t msg_11[] = {
+    0xE3, 0x35, 0x94, 0xD7, 0x50, 0x5E, 0x43, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x33, 0x94, 0xD7, 0x50, 0x5E, 0x43, 0x79, 0xCD, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t tag_11[] = {
+    0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void _test_poly1305(const uint8_t *key, const uint8_t *msg, size_t msglen, const uint8_t *tag)
+{
+    uint8_t gen_tag[16];
+
+    poly1305_auth(gen_tag, msg, msglen, key);
+    for (unsigned i = 0; i < sizeof(tag); i++) {
+        TEST_ASSERT_EQUAL_INT(gen_tag[i], tag[i]);
+    }
+}
+
+static void test_crypto_poly1305_1(void)
+{
+    _test_poly1305(key_1, msg_1, sizeof(msg_1), tag_1);
+}
+
+static void test_crypto_poly1305_2(void)
+{
+    _test_poly1305(key_2, msg_2, sizeof(msg_2), tag_2);
+}
+
+static void test_crypto_poly1305_3(void)
+{
+    _test_poly1305(key_3, msg_3, sizeof(msg_3), tag_3);
+}
+
+static void test_crypto_poly1305_4(void)
+{
+    _test_poly1305(key_4, msg_4, sizeof(msg_4), tag_4);
+}
+
+static void test_crypto_poly1305_5(void)
+{
+    _test_poly1305(key_5, msg_5, sizeof(msg_5), tag_5);
+}
+
+static void test_crypto_poly1305_6(void)
+{
+    _test_poly1305(key_6, msg_6, sizeof(msg_6), tag_6);
+}
+
+static void test_crypto_poly1305_7(void)
+{
+    _test_poly1305(key_7, msg_7, sizeof(msg_7), tag_7);
+}
+
+static void test_crypto_poly1305_8(void)
+{
+    _test_poly1305(key_8, msg_8, sizeof(msg_8), tag_8);
+}
+
+static void test_crypto_poly1305_9(void)
+{
+    _test_poly1305(key_9, msg_9, sizeof(msg_9), tag_9);
+}
+
+static void test_crypto_poly1305_10(void)
+{
+    _test_poly1305(key_10, msg_10, sizeof(msg_10), tag_10);
+}
+
+static void test_crypto_poly1305_11(void)
+{
+    _test_poly1305(key_11, msg_11, sizeof(msg_11), tag_11);
+}
+
+Test *tests_crypto_poly1305_tests(void)
+{
+    EMB_UNIT_TESTFIXTURES(fixtures) {
+        new_TestFixture(test_crypto_poly1305_1),
+        new_TestFixture(test_crypto_poly1305_2),
+        new_TestFixture(test_crypto_poly1305_3),
+        new_TestFixture(test_crypto_poly1305_4),
+        new_TestFixture(test_crypto_poly1305_5),
+        new_TestFixture(test_crypto_poly1305_6),
+        new_TestFixture(test_crypto_poly1305_7),
+        new_TestFixture(test_crypto_poly1305_8),
+        new_TestFixture(test_crypto_poly1305_9),
+        new_TestFixture(test_crypto_poly1305_10),
+        new_TestFixture(test_crypto_poly1305_11),
+    };
+    EMB_UNIT_TESTCALLER(crypto_poly1305_tests, NULL, NULL, fixtures);
+    return (Test *) &crypto_poly1305_tests;
+}
diff --git a/tests/unittests/tests-crypto/tests-crypto.c b/tests/unittests/tests-crypto/tests-crypto.c
index 38910467b2af9bdb17ae1c2b679f9df0298fc955..3756fae1a4b63be2eae67b292714053e5360c4e8 100644
--- a/tests/unittests/tests-crypto/tests-crypto.c
+++ b/tests/unittests/tests-crypto/tests-crypto.c
@@ -13,6 +13,7 @@ void tests_crypto(void)
 {
     TESTS_RUN(tests_crypto_helper_tests());
     TESTS_RUN(tests_crypto_chacha_tests());
+    TESTS_RUN(tests_crypto_poly1305_tests());
     TESTS_RUN(tests_crypto_aes_tests());
     TESTS_RUN(tests_crypto_cipher_tests());
     TESTS_RUN(tests_crypto_modes_ccm_tests());
diff --git a/tests/unittests/tests-crypto/tests-crypto.h b/tests/unittests/tests-crypto/tests-crypto.h
index 0c6c8ba953b5a5915a73bbbb488523a7f6af08b2..ad9700400542c5cd6c4bd108ee1daa3016c21497 100644
--- a/tests/unittests/tests-crypto/tests-crypto.h
+++ b/tests/unittests/tests-crypto/tests-crypto.h
@@ -46,6 +46,8 @@ Test *tests_crypto_helper_tests(void);
  */
 Test *tests_crypto_chacha_tests(void);
 
+Test *tests_crypto_poly1305_tests(void);
+
 static inline int compare(const uint8_t *a, const uint8_t *b, uint8_t len)
 {
     int result = 1;