diff --git a/handlers/Config.in b/handlers/Config.in
index 41eac1c..5a03740 100644
--- a/handlers/Config.in
+++ b/handlers/Config.in
@@ -106,6 +106,13 @@ config RDIFFHANDLER
 	  Add support for applying librsync's rdiff patches,
 	  see http://librsync.sourcefrog.net/
 
+config READBACKHANDLER
+	bool "readback"
+	select HASH_VERIFY
+	default n
+	help
+	  Use sha256 hash to verify a target partition.
+
 config LUASCRIPTHANDLER
 	bool "Lua Script"
 	depends on LUA
diff --git a/handlers/Makefile b/handlers/Makefile
index 61e4f76..b756f31 100644
--- a/handlers/Makefile
+++ b/handlers/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_CFIHAMMING1)+= flash_hamming1_handler.o
 obj-$(CONFIG_LUASCRIPTHANDLER) += lua_scripthandler.o
 obj-$(CONFIG_RAW)	+= raw_handler.o
 obj-$(CONFIG_RDIFFHANDLER) += rdiff_handler.o
+obj-$(CONFIG_READBACKHANDLER) += readback_handler.o
 obj-$(CONFIG_REMOTE_HANDLER) += remote_handler.o
 obj-$(CONFIG_SHELLSCRIPTHANDLER) += shell_scripthandler.o
 obj-$(CONFIG_SSBLSWITCH) += ssbl_handler.o
diff --git a/handlers/readback_handler.c b/handlers/readback_handler.c
new file mode 100644
index 0000000..2e4faa8
--- /dev/null
+++ b/handlers/readback_handler.c
@@ -0,0 +1,161 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Bosch Sicherheitssysteme GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "swupdate.h"
+#include "handler.h"
+#include "sslapi.h"
+#include "util.h"
+
+void readback_handler(void);
+static int readback_postinst(struct img_type *img);
+static int verify_file_hash(const char* filename, size_t size, long offset, unsigned char *hash);
+
+static int readback(struct img_type *img, void *data)
+{
+	if (!data)
+		return -1;
+
+	script_fn scriptfn = *(script_fn *)data;
+	switch (scriptfn) {
+	case POSTINSTALL:
+		return readback_postinst(img);
+	case PREINSTALL:
+	default:
+		return 0;
+	}
+}
+
+static int readback_postinst(struct img_type *img)
+{
+	/* Get file hash */
+	char *ascii_hash = dict_get_value(&img->properties, "sha256");
+	if (!ascii_hash) {
+		ERROR("Property sha256 not found");
+		return -EINVAL;
+	}
+
+	/* Get file size */
+	char *value = dict_get_value(&img->properties, "size");
+	if (!value) {
+		ERROR("Property size not found");
+		return -EINVAL;
+	}
+	unsigned long long size = ustrtoull(value, 10);
+
+	/* Get file offset */
+	value = dict_get_value(&img->properties, "offset");
+	if (!value) {
+		ERROR("Property offset not found");
+		return -EINVAL;
+	}
+	unsigned long long offset = ustrtoull(value, 10);
+
+	/* Convert the ascii hash to binary */
+	unsigned char hash[SHA256_HASH_LENGTH];
+	ascii_to_hash(hash, ascii_hash);
+
+	return verify_file_hash(img->device, size, offset, hash);
+}
+
+#define BUF_SIZE 4096
+
+static int verify_file_hash(const char* filename, size_t size, long offset, unsigned char *hash)
+{
+	int status = 0;
+	FILE *fp = NULL;
+	unsigned char *buf = NULL;
+	struct swupdate_digest *dgst = NULL;
+
+	if (IsValidHash(hash) == 0) {
+		ERROR("Invalid hash");
+		return -EINVAL;
+	}
+
+	if (!filename) {
+		ERROR("Invalid file name");
+		return -EINVAL;
+	}
+
+	fp = fopen(filename, "rb");
+	if (!fp) {
+		ERROR("Failed to open %s: %s", filename, strerror(errno));
+		status = -ENODEV;
+		goto cleanup;
+	}
+
+	if (fseek(fp, offset, SEEK_SET)) {
+		ERROR("Failed to seek to position %ld: %s", offset, strerror(errno));
+		status = -ENODEV;
+		goto cleanup;
+	}
+
+	dgst = swupdate_HASH_init(SHA_DEFAULT);
+	if (!dgst) {
+		status = -EFAULT;
+		goto cleanup;
+	}
+
+	buf = malloc(BUF_SIZE);
+	if (!buf) {
+		status = -ENOMEM;
+		goto cleanup;
+	}
+
+	size_t totalread = 0;
+	while (totalread < size) {
+		memset(buf, 0, BUF_SIZE);
+
+		size_t readsize = ((size - totalread) > BUF_SIZE) ?
+				BUF_SIZE : (size - totalread);
+
+		if (fread(buf, 1, readsize, fp) != readsize) {
+			ERROR("Failed to read %s: %s", filename, strerror(errno));
+			status = -EIO;
+			goto cleanup;
+		}
+		totalread += readsize;
+
+		if (swupdate_HASH_update(dgst, buf, readsize) < 0) {
+			status = -EFAULT;
+			goto cleanup;
+		}
+	}
+
+	unsigned int hash_len = 0;
+	unsigned char hash_value[SHA256_HASH_LENGTH];
+	if (swupdate_HASH_final(dgst, hash_value, &hash_len) < 0) {
+		status = -EFAULT;
+		goto cleanup;
+	}
+
+	if (hash_len != SHA256_HASH_LENGTH || swupdate_HASH_compare(hash_value, hash) < 0) {
+		ERROR("Hash does not match");
+		status = -EFAULT;
+		goto cleanup;
+	}
+
+	INFO("Verify file hash success: %s", filename);
+
+cleanup:
+	if (fp)
+		fclose(fp);
+	if (buf)
+		free(buf);
+	if (dgst)
+		swupdate_HASH_cleanup(dgst);
+
+	return status;
+}
+
+__attribute__((constructor))
+void readback_handler(void)
+{
+	register_handler("readback", readback, SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
+}
