diff --git a/block/generic-luks.c b/block/generic-luks.c
index ebc0365d40..32cbedc86f 100644
--- a/block/generic-luks.c
+++ b/block/generic-luks.c
@@ -23,8 +23,14 @@
 #include "qemu/osdep.h"
 
 #include "block/block_int.h"
+#include "block/block-io.h"
 #include "block/crypto.h"
+#include "block/qdict.h"
 #include "crypto/block.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/module.h"
+#include "qemu/option.h"
 
 #include "generic-luks.h"
 
@@ -50,10 +56,89 @@ static QemuOptsList gluks_create_opts_luks = {
     },
 };
 
+static int gluks_read_func(QCryptoBlock *block,
+                           size_t offset,
+                           uint8_t *buf,
+                           size_t buflen,
+                           void *opaque,
+                           Error **errp)
+{
+
+    BlockDriverState *bs = opaque;
+    BDRVGLUKSState *s = bs->opaque;
+    ssize_t ret;
+
+    GLOBAL_STATE_CODE();
+    GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+    ret = bdrv_pread(s->header, offset, buflen, buf, 0);
+    if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not read generic luks header");
+        return ret;
+    }
+    return 0;
+}
+
 static int gluks_open(BlockDriverState *bs, QDict *options, int flags,
                       Error **errp)
 {
-    return 0;
+    BDRVGLUKSState *s = bs->opaque;
+    QemuOpts *opts = NULL;
+    QCryptoBlockOpenOptions *open_opts = NULL;
+    QDict *cryptoopts = NULL;
+    unsigned int cflags = 0;
+    int ret;
+
+    GLOBAL_STATE_CODE();
+
+    if (!bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
+                         (BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY), false, errp)) {
+        return -EINVAL;
+    }
+    s->header = bdrv_open_child(NULL, options, "header", bs,
+                                &child_of_bds, BDRV_CHILD_METADATA, false,
+                                errp);
+    if (!s->header) {
+        return -EINVAL;
+    }
+
+    GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+    opts = qemu_opts_create(&block_crypto_runtime_opts_luks,
+        NULL, 0, &error_abort);
+    if (!qemu_opts_absorb_qdict(opts, options, errp)) {
+        ret = -EINVAL;
+        goto cleanup;
+    }
+
+    cryptoopts = qemu_opts_to_qdict(opts, NULL);
+    qdict_put_str(cryptoopts, "format",
+        QCryptoBlockFormat_str(Q_CRYPTO_BLOCK_FORMAT_GLUKS));
+
+    open_opts = block_crypto_open_opts_init(cryptoopts, errp);
+    if (!open_opts) {
+        goto cleanup;
+    }
+
+    s->crypto.block = qcrypto_block_open(open_opts, NULL,
+                                         gluks_read_func,
+                                         bs,
+                                         cflags,
+                                         1,
+                                         errp);
+    if (!s->crypto.block) {
+        ret = -EIO;
+        goto cleanup;
+    }
+
+    s->header_size = qcrypto_block_get_payload_offset(s->crypto.block);
+    qcrypto_block_set_payload_offset(s->crypto.block, 0);
+
+    ret = 0;
+ cleanup:
+    qobject_unref(cryptoopts);
+    qapi_free_QCryptoBlockOpenOptions(open_opts);
+    return ret;
 }
 
 static int coroutine_fn GRAPH_UNLOCKED
@@ -70,13 +155,24 @@ gluks_child_perms(BlockDriverState *bs, BdrvChild *c,
                   uint64_t perm, uint64_t shared,
                   uint64_t *nperm, uint64_t *nshared)
 {
+    if (role & BDRV_CHILD_METADATA) {
+        /* assign read permission only */
+        perm |= BLK_PERM_CONSISTENT_READ;
+        /* share all permissions */
+        shared |= BLK_PERM_ALL;
 
+        *nperm = perm;
+        *nshared = shared;
+        return;
+    }
+
+    bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared);
 }
 
 static int64_t coroutine_fn GRAPH_RDLOCK
 gluks_co_getlength(BlockDriverState *bs)
 {
-    return 0;
+    return bdrv_co_getlength(bs->file->bs);
 }
 
 static BlockDriver bdrv_generic_luks = {
@@ -87,8 +183,12 @@ static BlockDriver bdrv_generic_luks = {
     .bdrv_child_perm        = gluks_child_perms,
     .bdrv_co_getlength      = gluks_co_getlength,
 
+    .bdrv_close             = block_crypto_close,
+    .bdrv_co_preadv         = block_crypto_co_preadv,
+    .bdrv_co_pwritev        = block_crypto_co_pwritev,
     .create_opts            = &gluks_create_opts_luks,
     .amend_opts             = &block_crypto_amend_opts_luks,
+    .is_format              = false,
 };
 
 static void block_generic_luks_init(void)
