diff --git a/Kconfig b/Kconfig
index 882ee1e..e9c535d 100644
--- a/Kconfig
+++ b/Kconfig
@@ -61,6 +61,10 @@ config HAVE_ZLIB
 	bool
 	option env="HAVE_ZLIB"
 
+config HAVE_ZSTD
+	bool
+	option env="HAVE_ZSTD"
+
 config HAVE_LIBSSL
 	bool
 	option env="HAVE_LIBSSL"
@@ -441,10 +445,21 @@ source suricatta/Config.in
 
 source mongoose/Config.in
 
+choice
+	prompt "Compression implementation to use"
+	default GUNZIP
+	help
+	  Select compression implementation for decompressing images.
+
 config GUNZIP
-	bool
+	bool "GUNZIP"
 	depends on HAVE_ZLIB
-	default y
+
+config ZSTD
+	bool "ZSTD"
+	depends on HAVE_ZSTD
+
+endchoice
 
 source parser/Config.in
 source handlers/Config.in
diff --git a/Makefile.deps b/Makefile.deps
index 512d4b8..5e2bd2f 100644
--- a/Makefile.deps
+++ b/Makefile.deps
@@ -38,6 +38,10 @@ ifeq ($(HAVE_ZLIB),)
 export HAVE_ZLIB = y
 endif
 
+ifeq ($(HAVE_ZSTD),)
+export HAVE_ZSTD = y
+endif
+
 ifeq ($(HAVE_LIBUBOOTENV),)
 export HAVE_LIBUBOOTENV = y
 endif
diff --git a/Makefile.flags b/Makefile.flags
index 0edd48c..38db720 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -162,6 +162,10 @@ ifeq ($(CONFIG_GUNZIP),y)
 LDLIBS += z
 endif
 
+ifeq ($(CONFIG_ZSTD),y)
+LDLIBS += zstd
+endif
+
 ifeq ($(CONFIG_RDIFFHANDLER),y)
 LDLIBS += rsync
 endif
diff --git a/core/cpio_utils.c b/core/cpio_utils.c
index 1b6cb14..9332a4c 100644
--- a/core/cpio_utils.c
+++ b/core/cpio_utils.c
@@ -10,8 +10,10 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <errno.h>
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP)
 #include <zlib.h>
+#elif defined(CONFIG_ZSTD)
+#include <zstd.h>
 #endif
 
 #include "generated/autoconf.h"
@@ -233,7 +235,7 @@ static int decrypt_step(void *state, void *buffer, size_t size)
 	return 0;
 }
 
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP)
 
 struct GunzipState
 {
@@ -283,6 +285,55 @@ static int gunzip_step(void *state, void *buffer, size_t size)
 	return outlen;
 }
 
+#elif defined(CONFIG_ZSTD)
+
+struct ZstdState
+{
+	PipelineStep upstream_step;
+	void *upstream_state;
+
+	ZSTD_DStream* dctx;
+	ZSTD_inBuffer input;
+	uint8_t input_buffer[BUFF_SIZE];
+	bool eof;
+};
+
+static int zstd_step(void* state, void* buffer, size_t size)
+{
+	struct ZstdState *s = (struct ZstdState *)state;
+	size_t decompress_ret;
+	int ret;
+	ZSTD_outBuffer output = { buffer, size, 0 };
+
+	do {
+		if (s->input.pos == s->input.size) {
+			ret = s->upstream_step(s->upstream_state, s->input_buffer, sizeof s->input_buffer);
+			if (ret < 0) {
+				return ret;
+			} else if (ret == 0) {
+				s->eof = true;
+			}
+			s->input.size = ret;
+			s->input.pos = 0;
+		}
+
+		do {
+			decompress_ret = ZSTD_decompressStream(s->dctx, &output, &s->input);
+
+			if (ZSTD_isError(decompress_ret)) {
+				ERROR("ZSTD_decompressStream failed (returned %zu)", decompress_ret);
+				return -1;
+			}
+
+			if (output.pos == output.size) {
+				break;
+			}
+		} while (s->input.pos < s->input.size);
+	} while (output.pos == 0 && !s->eof);
+
+	return output.pos;
+}
+
 #endif
 
 int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsigned long long seek,
@@ -314,7 +365,7 @@ int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 		.outlen = 0, .eof = false
 	};
 
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP)
 	struct GunzipState gunzip_state = {
 		.upstream_step = NULL, .upstream_state = NULL,
 		.strm = {
@@ -325,6 +376,13 @@ int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 		.initialized = false,
 		.eof = false
 	};
+#elif defined(CONFIG_ZSTD)
+	struct ZstdState zstd_state = {
+		.upstream_step = NULL, .upstream_state = NULL,
+		.dctx = NULL,
+		.input = { NULL, 0, 0 },
+		.eof = false
+	};
 #endif
 
 	PipelineStep step = NULL;
@@ -356,7 +414,7 @@ int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 	}
 
 	if (compressed) {
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP)
 		/*
 		 * 16 + MAX_WBITS means that Zlib should expect and decode a
 		 * gzip header.
@@ -367,8 +425,15 @@ int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 			goto copyfile_exit;
 		}
 		gunzip_state.initialized = true;
+#elif defined(CONFIG_ZSTD)
+		if ((zstd_state.dctx = ZSTD_createDStream()) == NULL) {
+			ERROR("ZSTD_createDStream failed");
+			ret = -EFAULT;
+			goto copyfile_exit;
+		}
+		zstd_state.input.src = zstd_state.input_buffer;
 #else
-		TRACE("Request decompressing, but CONFIG_GUNZIP not set !");
+		TRACE("Request decompressing, but CONFIG_GUNZIP or CONFIG_ZSTD not set !");
 		ret = -EINVAL;
 		goto copyfile_exit;
 #endif
@@ -384,7 +449,7 @@ int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 		}
 	}
 
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP)
 	if (compressed) {
 		if (encrypted) {
 			decrypt_state.upstream_step = &input_step;
@@ -398,6 +463,20 @@ int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 		step = &gunzip_step;
 		state = &gunzip_state;
 	} else {
+#elif defined(CONFIG_ZSTD)
+	if (compressed) {
+		if (encrypted) {
+			decrypt_state.upstream_step = &input_step;
+			decrypt_state.upstream_state = &input_state;
+			zstd_state.upstream_step = &decrypt_step;
+			zstd_state.upstream_state = &decrypt_state;
+		} else {
+			zstd_state.upstream_step = &input_step;
+			zstd_state.upstream_state = &input_state;
+		}
+		step = &zstd_step;
+		state = &zstd_state;
+	} else {
 #endif
 		if (encrypted) {
 			decrypt_state.upstream_step = &input_step;
@@ -408,7 +487,7 @@ int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 			step = &input_step;
 			state = &input_state;
 		}
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
 	}
 #endif
 
@@ -482,10 +561,14 @@ copyfile_exit:
 	if (input_state.dgst) {
 		swupdate_HASH_cleanup(input_state.dgst);
 	}
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP)
 	if (gunzip_state.initialized) {
 		inflateEnd(&gunzip_state.strm);
 	}
+#elif defined(CONFIG_ZSTD)
+	if (zstd_state.dctx != NULL) {
+		ZSTD_freeDStream(zstd_state.dctx);
+	}
 #endif
 
 	return ret;
