Imported Upstream version 2.6.1
[platform/upstream/cryptsetup.git] / lib / luks2 / luks2_json_metadata.c
index e346067..4771f04 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * LUKS - Linux Unified Key Setup v2
  *
- * Copyright (C) 2015-2020 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2020 Milan Broz
- * Copyright (C) 2015-2020 Ondrej Kozina
+ * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2023 Ondrej Kozina
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -22,7 +22,6 @@
 
 #include "luks2_internal.h"
 #include "../integrity/integrity.h"
-#include <assert.h>
 #include <ctype.h>
 #include <uuid/uuid.h>
 
@@ -40,9 +39,8 @@ void hexprint_base64(struct crypt_device *cd, json_object *jobj,
        size_t buf_len;
        unsigned int i;
 
-       if (!base64_decode_alloc(json_object_get_string(jobj),
-                                json_object_get_string_len(jobj),
-                                &buf, &buf_len))
+       if (crypt_base64_decode(&buf, &buf_len, json_object_get_string(jobj),
+                               json_object_get_string_len(jobj)))
                return;
 
        for (i = 0; i < buf_len; i++) {
@@ -209,7 +207,7 @@ int LUKS2_get_default_segment(struct luks2_hdr *hdr)
        if (s >= 0)
                return s;
 
-       if (LUKS2_segments_count(hdr) == 1)
+       if (LUKS2_segments_count(hdr) >= 1)
                return 0;
 
        return -EINVAL;
@@ -225,7 +223,7 @@ uint32_t crypt_jobj_get_uint32(json_object *jobj)
 }
 
 /* jobj has to be json_type_string and numbered */
-static json_bool json_str_to_uint64(json_object *jobj, uint64_t *value)
+static bool json_str_to_uint64(json_object *jobj, uint64_t *value)
 {
        char *endptr;
        unsigned long long tmp;
@@ -234,11 +232,11 @@ static json_bool json_str_to_uint64(json_object *jobj, uint64_t *value)
        tmp = strtoull(json_object_get_string(jobj), &endptr, 10);
        if (*endptr || errno) {
                *value = 0;
-               return 0;
+               return false;
        }
 
        *value = tmp;
-       return 1;
+       return true;
 }
 
 uint64_t crypt_jobj_get_uint64(json_object *jobj)
@@ -266,16 +264,16 @@ json_object *crypt_jobj_new_uint64(uint64_t value)
 /*
  * Validate helpers
  */
-static json_bool numbered(struct crypt_device *cd, const char *name, const char *key)
+static bool numbered(struct crypt_device *cd, const char *name, const char *key)
 {
        int i;
 
        for (i = 0; key[i]; i++)
                if (!isdigit(key[i])) {
                        log_dbg(cd, "%s \"%s\" is not in numbered form.", name, key);
-                       return 0;
+                       return false;
                }
-       return 1;
+       return true;
 }
 
 json_object *json_contains(struct crypt_device *cd, json_object *jobj, const char *name,
@@ -293,18 +291,31 @@ json_object *json_contains(struct crypt_device *cd, json_object *jobj, const cha
        return sobj;
 }
 
-json_bool validate_json_uint32(json_object *jobj)
+json_object *json_contains_string(struct crypt_device *cd, json_object *jobj,
+                                 const char *name, const char *section, const char *key)
+{
+       json_object *sobj = json_contains(cd, jobj, name, section, key, json_type_string);
+
+       if (!sobj)
+               return NULL;
+
+       if (strlen(json_object_get_string(sobj)) < 1)
+               return NULL;
+
+       return sobj;
+}
+
+bool validate_json_uint32(json_object *jobj)
 {
        int64_t tmp;
 
        errno = 0;
        tmp = json_object_get_int64(jobj);
 
-       return (errno || tmp < 0 || tmp > UINT32_MAX) ? 0 : 1;
+       return (errno || tmp < 0 || tmp > UINT32_MAX) ? false : true;
 }
 
-static json_bool validate_keyslots_array(struct crypt_device *cd,
-                                        json_object *jarr, json_object *jobj_keys)
+static bool validate_keyslots_array(struct crypt_device *cd, json_object *jarr, json_object *jobj_keys)
 {
        json_object *jobj;
        int i = 0, length = (int) json_object_array_length(jarr);
@@ -313,21 +324,20 @@ static json_bool validate_keyslots_array(struct crypt_device *cd,
                jobj = json_object_array_get_idx(jarr, i);
                if (!json_object_is_type(jobj, json_type_string)) {
                        log_dbg(cd, "Illegal value type in keyslots array at index %d.", i);
-                       return 0;
+                       return false;
                }
 
                if (!json_contains(cd, jobj_keys, "", "Keyslots section",
                                   json_object_get_string(jobj), json_type_object))
-                       return 0;
+                       return false;
 
                i++;
        }
 
-       return 1;
+       return true;
 }
 
-static json_bool validate_segments_array(struct crypt_device *cd,
-                                        json_object *jarr, json_object *jobj_segments)
+static bool validate_segments_array(struct crypt_device *cd, json_object *jarr, json_object *jobj_segments)
 {
        json_object *jobj;
        int i = 0, length = (int) json_object_array_length(jarr);
@@ -336,20 +346,20 @@ static json_bool validate_segments_array(struct crypt_device *cd,
                jobj = json_object_array_get_idx(jarr, i);
                if (!json_object_is_type(jobj, json_type_string)) {
                        log_dbg(cd, "Illegal value type in segments array at index %d.", i);
-                       return 0;
+                       return false;
                }
 
                if (!json_contains(cd, jobj_segments, "", "Segments section",
                                   json_object_get_string(jobj), json_type_object))
-                       return 0;
+                       return false;
 
                i++;
        }
 
-       return 1;
+       return true;
 }
 
-static json_bool segment_has_digest(const char *segment_name, json_object *jobj_digests)
+static bool segment_has_digest(const char *segment_name, json_object *jobj_digests)
 {
        json_object *jobj_segments;
 
@@ -357,57 +367,70 @@ static json_bool segment_has_digest(const char *segment_name, json_object *jobj_
                UNUSED(key);
                json_object_object_get_ex(val, "segments", &jobj_segments);
                if (LUKS2_array_jobj(jobj_segments, segment_name))
-                       return 1;
+                       return true;
        }
 
-       return 0;
+       return false;
 }
 
-static json_bool validate_intervals(struct crypt_device *cd,
-                                   int length, const struct interval *ix,
-                                   uint64_t metadata_size, uint64_t keyslots_area_end)
+
+static bool validate_intervals(struct crypt_device *cd,
+                              int length, const struct interval *ix,
+                              uint64_t metadata_size, uint64_t keyslots_area_end)
 {
        int j, i = 0;
 
        while (i < length) {
+               /* Offset cannot be inside primary or secondary JSON area */
                if (ix[i].offset < 2 * metadata_size) {
                        log_dbg(cd, "Illegal area offset: %" PRIu64 ".", ix[i].offset);
-                       return 0;
+                       return false;
                }
 
                if (!ix[i].length) {
                        log_dbg(cd, "Area length must be greater than zero.");
-                       return 0;
+                       return false;
+               }
+
+               if (ix[i].offset > (UINT64_MAX - ix[i].length)) {
+                       log_dbg(cd, "Interval offset+length overflow.");
+                       return false;
                }
 
                if ((ix[i].offset + ix[i].length) > keyslots_area_end) {
                        log_dbg(cd, "Area [%" PRIu64 ", %" PRIu64 "] overflows binary keyslots area (ends at offset: %" PRIu64 ").",
                                ix[i].offset, ix[i].offset + ix[i].length, keyslots_area_end);
-                       return 0;
+                       return false;
                }
 
                for (j = 0; j < length; j++) {
                        if (i == j)
                                continue;
+
+                       if (ix[j].offset > (UINT64_MAX - ix[j].length)) {
+                               log_dbg(cd, "Interval offset+length overflow.");
+                               return false;
+                       }
+
                        if ((ix[i].offset >= ix[j].offset) && (ix[i].offset < (ix[j].offset + ix[j].length))) {
                                log_dbg(cd, "Overlapping areas [%" PRIu64 ",%" PRIu64 "] and [%" PRIu64 ",%" PRIu64 "].",
                                        ix[i].offset, ix[i].offset + ix[i].length,
                                        ix[j].offset, ix[j].offset + ix[j].length);
-                               return 0;
+                               return false;
                        }
                }
 
                i++;
        }
 
-       return 1;
+       return true;
 }
 
-static int LUKS2_keyslot_validate(struct crypt_device *cd, json_object *hdr_jobj, json_object *hdr_keyslot, const char *key)
+static int LUKS2_keyslot_validate(struct crypt_device *cd, json_object *hdr_keyslot, const char *key)
 {
        json_object *jobj_key_size;
 
-       if (!json_contains(cd, hdr_keyslot, key, "Keyslot", "type", json_type_string))
+       if (!json_contains_string(cd, hdr_keyslot, key, "Keyslot", "type"))
                return 1;
        if (!(jobj_key_size = json_contains(cd, hdr_keyslot, key, "Keyslot", "key_size", json_type_int)))
                return 1;
@@ -431,7 +454,7 @@ int LUKS2_token_validate(struct crypt_device *cd,
        if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
                return 1;
 
-       if (!json_contains(cd, jobj_token, key, "Token", "type", json_type_string))
+       if (!json_contains_string(cd, jobj_token, key, "Token", "type"))
                return 1;
 
        jarr = json_contains(cd, jobj_token, key, "Token", "keyslots", json_type_array);
@@ -480,15 +503,13 @@ static int hdr_validate_keyslots(struct crypt_device *cd, json_object *hdr_jobj)
 {
        json_object *jobj;
 
-       if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj)) {
-               log_dbg(cd, "Missing keyslots section.");
+       if (!(jobj = json_contains(cd, hdr_jobj, "", "JSON area", "keyslots", json_type_object)))
                return 1;
-       }
 
        json_object_object_foreach(jobj, key, val) {
                if (!numbered(cd, "Keyslot", key))
                        return 1;
-               if (LUKS2_keyslot_validate(cd, hdr_jobj, val, key))
+               if (LUKS2_keyslot_validate(cd, val, key))
                        return 1;
        }
 
@@ -499,10 +520,8 @@ static int hdr_validate_tokens(struct crypt_device *cd, json_object *hdr_jobj)
 {
        json_object *jobj;
 
-       if (!json_object_object_get_ex(hdr_jobj, "tokens", &jobj)) {
-               log_dbg(cd, "Missing tokens section.");
+       if (!(jobj = json_contains(cd, hdr_jobj, "", "JSON area", "tokens", json_type_object)))
                return 1;
-       }
 
        json_object_object_foreach(jobj, key, val) {
                if (!numbered(cd, "Token", key))
@@ -514,25 +533,26 @@ static int hdr_validate_tokens(struct crypt_device *cd, json_object *hdr_jobj)
        return 0;
 }
 
-static int hdr_validate_crypt_segment(struct crypt_device *cd,
-                                     json_object *jobj, const char *key, json_object *jobj_digests,
-       uint64_t offset, uint64_t size)
+static int hdr_validate_crypt_segment(struct crypt_device *cd, json_object *jobj,
+                                     const char *key, json_object *jobj_digests,
+                                     uint64_t size)
 {
+       int r;
        json_object *jobj_ivoffset, *jobj_sector_size, *jobj_integrity;
        uint32_t sector_size;
        uint64_t ivoffset;
 
-       if (!(jobj_ivoffset = json_contains(cd, jobj, key, "Segment", "iv_tweak", json_type_string)) ||
-           !json_contains(cd, jobj, key, "Segment", "encryption", json_type_string) ||
+       if (!(jobj_ivoffset = json_contains_string(cd, jobj, key, "Segment", "iv_tweak")) ||
+           !json_contains_string(cd, jobj, key, "Segment", "encryption") ||
            !(jobj_sector_size = json_contains(cd, jobj, key, "Segment", "sector_size", json_type_int)))
                return 1;
 
        /* integrity */
        if (json_object_object_get_ex(jobj, "integrity", &jobj_integrity)) {
                if (!json_contains(cd, jobj, key, "Segment", "integrity", json_type_object) ||
-                   !json_contains(cd, jobj_integrity, key, "Segment integrity", "type", json_type_string) ||
-                   !json_contains(cd, jobj_integrity, key, "Segment integrity", "journal_encryption", json_type_string) ||
-                   !json_contains(cd, jobj_integrity, key, "Segment integrity", "journal_integrity", json_type_string))
+                   !json_contains_string(cd, jobj_integrity, key, "Segment integrity", "type") ||
+                   !json_contains_string(cd, jobj_integrity, key, "Segment integrity", "journal_encryption") ||
+                   !json_contains_string(cd, jobj_integrity, key, "Segment integrity", "journal_integrity"))
                        return 1;
        }
 
@@ -560,7 +580,12 @@ static int hdr_validate_crypt_segment(struct crypt_device *cd,
                return 1;
        }
 
-       return !segment_has_digest(key, jobj_digests);
+       r = segment_has_digest(key, jobj_digests);
+
+       if (!r)
+               log_dbg(cd, "Crypt segment %s not assigned to key digest.", key);
+
+       return !r;
 }
 
 static bool validate_segment_intervals(struct crypt_device *cd,
@@ -577,6 +602,12 @@ static bool validate_segment_intervals(struct crypt_device *cd,
                for (j = 0; j < length; j++) {
                        if (i == j)
                                continue;
+
+                       if (ix[j].length != UINT64_MAX && ix[j].offset > (UINT64_MAX - ix[j].length)) {
+                               log_dbg(cd, "Interval offset+length overflow.");
+                               return false;
+                       }
+
                        if ((ix[i].offset >= ix[j].offset) && (ix[j].length == UINT64_MAX || (ix[i].offset < (ix[j].offset + ix[j].length)))) {
                                log_dbg(cd, "Overlapping segments [%" PRIu64 ",%" PRIu64 "]%s and [%" PRIu64 ",%" PRIu64 "]%s.",
                                        ix[i].offset, ix[i].offset + ix[i].length, ix[i].length == UINT64_MAX ? "(dynamic)" : "",
@@ -591,17 +622,87 @@ static bool validate_segment_intervals(struct crypt_device *cd,
        return true;
 }
 
+static int reqs_unknown(uint32_t reqs)
+{
+       return reqs & CRYPT_REQUIREMENT_UNKNOWN;
+}
+
+static int reqs_reencrypt(uint32_t reqs)
+{
+       return reqs & CRYPT_REQUIREMENT_OFFLINE_REENCRYPT;
+}
+
+static int reqs_reencrypt_online(uint32_t reqs)
+{
+       return reqs & CRYPT_REQUIREMENT_ONLINE_REENCRYPT;
+}
+
+/*
+ * Config section requirements object must be valid.
+ * Also general segments section must be validated first.
+ */
+static int validate_reencrypt_segments(struct crypt_device *cd, json_object *hdr_jobj, json_object *jobj_segments, int first_backup, int segments_count)
+{
+       json_object *jobj, *jobj_backup_previous = NULL, *jobj_backup_final = NULL;
+       uint32_t reqs;
+       int i, r;
+       struct luks2_hdr dummy = {
+               .jobj = hdr_jobj
+       };
+
+       r = LUKS2_config_get_requirements(cd, &dummy, &reqs);
+       if (r)
+               return 1;
+
+       if (reqs_reencrypt_online(reqs)) {
+               for (i = first_backup; i < segments_count; i++) {
+                       jobj = json_segments_get_segment(jobj_segments, i);
+                       if (!jobj)
+                               return 1;
+                       if (json_segment_contains_flag(jobj, "backup-final", 0))
+                               jobj_backup_final = jobj;
+                       else if (json_segment_contains_flag(jobj, "backup-previous", 0))
+                               jobj_backup_previous = jobj;
+               }
+
+               if (!jobj_backup_final || !jobj_backup_previous) {
+                       log_dbg(cd, "Backup segment is missing.");
+                       return 1;
+               }
+
+               for (i = 0; i < first_backup; i++) {
+                       jobj = json_segments_get_segment(jobj_segments, i);
+                       if (!jobj)
+                               return 1;
+
+                       if (json_segment_contains_flag(jobj, "in-reencryption", 0)) {
+                               if (!json_segment_cmp(jobj, jobj_backup_final)) {
+                                       log_dbg(cd, "Segment in reencryption does not match backup final segment.");
+                                       return 1;
+                               }
+                               continue;
+                       }
+
+                       if (!json_segment_cmp(jobj, jobj_backup_final) &&
+                           !json_segment_cmp(jobj, jobj_backup_previous)) {
+                               log_dbg(cd, "Segment does not match neither backup final or backup previous segment.");
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
 {
        json_object *jobj_segments, *jobj_digests, *jobj_offset, *jobj_size, *jobj_type, *jobj_flags, *jobj;
-       struct interval *intervals;
        uint64_t offset, size;
        int i, r, count, first_backup = -1;
+       struct interval *intervals = NULL;
 
-       if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments)) {
-               log_dbg(cd, "Missing segments section.");
+       if (!(jobj_segments = json_contains(cd, hdr_jobj, "", "JSON area", "segments", json_type_object)))
                return 1;
-       }
 
        count = json_object_object_length(jobj_segments);
        if (count < 1) {
@@ -618,20 +719,27 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
                        return 1;
 
                /* those fields are mandatory for all segment types */
-               if (!(jobj_type =   json_contains(cd, val, key, "Segment", "type",   json_type_string)) ||
-                   !(jobj_offset = json_contains(cd, val, key, "Segment", "offset", json_type_string)) ||
-                   !(jobj_size =   json_contains(cd, val, key, "Segment", "size",   json_type_string)))
+               if (!(jobj_type =   json_contains_string(cd, val, key, "Segment", "type")) ||
+                   !(jobj_offset = json_contains_string(cd, val, key, "Segment", "offset")) ||
+                   !(jobj_size =   json_contains_string(cd, val, key, "Segment", "size")))
                        return 1;
 
-               if (!numbered(cd, "offset", json_object_get_string(jobj_offset)) ||
-                   !json_str_to_uint64(jobj_offset, &offset))
+               if (!numbered(cd, "offset", json_object_get_string(jobj_offset)))
                        return 1;
 
+               if (!json_str_to_uint64(jobj_offset, &offset)) {
+                       log_dbg(cd, "Illegal segment offset value.");
+                       return 1;
+               }
+
                /* size "dynamic" means whole device starting at 'offset' */
                if (strcmp(json_object_get_string(jobj_size), "dynamic")) {
-                       if (!numbered(cd, "size", json_object_get_string(jobj_size)) ||
-                           !json_str_to_uint64(jobj_size, &size) || !size)
+                       if (!numbered(cd, "size", json_object_get_string(jobj_size)))
+                               return 1;
+                       if (!json_str_to_uint64(jobj_size, &size) || !size) {
+                               log_dbg(cd, "Illegal segment size value.");
                                return 1;
+                       }
                } else
                        size = 0;
 
@@ -667,7 +775,7 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
 
                /* crypt */
                if (!strcmp(json_object_get_string(jobj_type), "crypt") &&
-                   hdr_validate_crypt_segment(cd, val, key, jobj_digests, offset, size))
+                   hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
                        return 1;
        }
 
@@ -676,10 +784,18 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
                return 1;
        }
 
+       /* avoid needlessly large allocation when first backup segment is invalid */
+       if (first_backup >= count) {
+               log_dbg(cd, "Gap between last regular segment and backup segment at key %d.", first_backup);
+               return 1;
+       }
+
        if (first_backup < 0)
                first_backup = count;
 
-       intervals = malloc(first_backup * sizeof(*intervals));
+       if ((size_t)first_backup < SIZE_MAX / sizeof(*intervals))
+               intervals = malloc(first_backup * sizeof(*intervals));
+
        if (!intervals) {
                log_dbg(cd, "Not enough memory.");
                return 1;
@@ -709,10 +825,10 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
                }
        }
 
-       return 0;
+       return validate_reencrypt_segments(cd, hdr_jobj, jobj_segments, first_backup, count);
 }
 
-uint64_t LUKS2_metadata_size(json_object *jobj)
+static uint64_t LUKS2_metadata_size_jobj(json_object *jobj)
 {
        json_object *jobj1, *jobj2;
        uint64_t json_size;
@@ -724,6 +840,11 @@ uint64_t LUKS2_metadata_size(json_object *jobj)
        return json_size + LUKS2_HDR_BIN_LEN;
 }
 
+uint64_t LUKS2_metadata_size(struct luks2_hdr *hdr)
+{
+       return LUKS2_metadata_size_jobj(hdr->jobj);
+}
+
 static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj)
 {
        struct interval *intervals;
@@ -739,7 +860,7 @@ static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj)
                return 1;
 
        /* config is already validated */
-       metadata_size = LUKS2_metadata_size(hdr_jobj);
+       metadata_size = LUKS2_metadata_size_jobj(hdr_jobj);
 
        length = json_object_object_length(jobj_keyslots);
 
@@ -761,9 +882,9 @@ static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj)
        json_object_object_foreach(jobj_keyslots, key, val) {
 
                if (!(jobj_area = json_contains(cd, val, key, "Keyslot", "area", json_type_object)) ||
-                   !json_contains(cd, jobj_area, key, "Keyslot area", "type", json_type_string) ||
-                   !(jobj_offset = json_contains(cd, jobj_area, key, "Keyslot", "offset", json_type_string)) ||
-                   !(jobj_length = json_contains(cd, jobj_area, key, "Keyslot", "size", json_type_string)) ||
+                   !json_contains_string(cd, jobj_area, key, "Keyslot area", "type") ||
+                   !(jobj_offset = json_contains_string(cd, jobj_area, key, "Keyslot", "offset")) ||
+                   !(jobj_length = json_contains_string(cd, jobj_area, key, "Keyslot", "size")) ||
                    !numbered(cd, "offset", json_object_get_string(jobj_offset)) ||
                    !numbered(cd, "size", json_object_get_string(jobj_length))) {
                        free(intervals);
@@ -773,6 +894,7 @@ static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj)
                /* rule out values > UINT64_MAX */
                if (!json_str_to_uint64(jobj_offset, &intervals[i].offset) ||
                    !json_str_to_uint64(jobj_length, &intervals[i].length)) {
+                       log_dbg(cd, "Illegal keyslot area values.");
                        free(intervals);
                        return 1;
                }
@@ -785,7 +907,7 @@ static int hdr_validate_areas(struct crypt_device *cd, json_object *hdr_jobj)
                return 1;
        }
 
-       ret = validate_intervals(cd, length, intervals, metadata_size, LUKS2_hdr_and_areas_size(hdr_jobj)) ? 0 : 1;
+       ret = validate_intervals(cd, length, intervals, metadata_size, LUKS2_hdr_and_areas_size_jobj(hdr_jobj)) ? 0 : 1;
 
        free(intervals);
 
@@ -796,24 +918,22 @@ static int hdr_validate_digests(struct crypt_device *cd, json_object *hdr_jobj)
 {
        json_object *jarr_keys, *jarr_segs, *jobj, *jobj_keyslots, *jobj_segments;
 
-       if (!json_object_object_get_ex(hdr_jobj, "digests", &jobj)) {
-               log_dbg(cd, "Missing digests section.");
+       if (!(jobj = json_contains(cd, hdr_jobj, "", "JSON area", "digests", json_type_object)))
                return 1;
-       }
 
        /* keyslots are not yet validated, but we need to know digest doesn't reference missing keyslot */
-       if (!json_object_object_get_ex(hdr_jobj, "keyslots", &jobj_keyslots))
+       if (!(jobj_keyslots = json_contains(cd, hdr_jobj, "", "JSON area", "keyslots", json_type_object)))
                return 1;
 
        /* segments are not yet validated, but we need to know digest doesn't reference missing segment */
-       if (!json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments))
+       if (!(jobj_segments = json_contains(cd, hdr_jobj, "", "JSON area", "segments", json_type_object)))
                return 1;
 
        json_object_object_foreach(jobj, key, val) {
                if (!numbered(cd, "Digest", key))
                        return 1;
 
-               if (!json_contains(cd, val, key, "Digest", "type", json_type_string) ||
+               if (!json_contains_string(cd, val, key, "Digest", "type") ||
                    !(jarr_keys = json_contains(cd, val, key, "Digest", "keyslots", json_type_array)) ||
                    !(jarr_segs = json_contains(cd, val, key, "Digest", "segments", json_type_array)))
                        return 1;
@@ -827,28 +947,33 @@ static int hdr_validate_digests(struct crypt_device *cd, json_object *hdr_jobj)
        return 0;
 }
 
+/* requirements being validated in stand-alone routine */
 static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj)
 {
-       json_object *jobj_config, *jobj, *jobj1;
+       json_object *jobj_config, *jobj;
        int i;
        uint64_t keyslots_size, metadata_size, segment_offset;
 
-       if (!json_object_object_get_ex(hdr_jobj, "config", &jobj_config)) {
-               log_dbg(cd, "Missing config section.");
+       if (!(jobj_config = json_contains(cd, hdr_jobj, "", "JSON area", "config", json_type_object)))
                return 1;
-       }
 
-       if (!(jobj = json_contains(cd, jobj_config, "section", "Config", "json_size", json_type_string)) ||
-           !json_str_to_uint64(jobj, &metadata_size))
+       if (!(jobj = json_contains_string(cd, jobj_config, "section", "Config", "json_size")))
+               return 1;
+       if (!json_str_to_uint64(jobj, &metadata_size)) {
+               log_dbg(cd, "Illegal config json_size value.");
                return 1;
+       }
 
        /* single metadata instance is assembled from json area size plus
         * binary header size */
        metadata_size += LUKS2_HDR_BIN_LEN;
 
-       if (!(jobj = json_contains(cd, jobj_config, "section", "Config", "keyslots_size", json_type_string)) ||
-           !json_str_to_uint64(jobj, &keyslots_size))
+       if (!(jobj = json_contains_string(cd, jobj_config, "section", "Config", "keyslots_size")))
                return 1;
+       if(!json_str_to_uint64(jobj, &keyslots_size)) {
+               log_dbg(cd, "Illegal config keyslot_size value.");
+               return 1;
+       }
 
        if (LUKS2_check_metadata_area_size(metadata_size)) {
                log_dbg(cd, "Unsupported LUKS2 header size (%" PRIu64 ").", metadata_size);
@@ -884,6 +1009,43 @@ static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj)
                                return 1;
        }
 
+       return 0;
+}
+
+static bool reencrypt_candidate_flag(const char *flag)
+{
+       const char *ptr;
+
+       assert(flag);
+
+       if (!strcmp(flag, "online-reencrypt"))
+               return true;
+
+       if (strncmp(flag, "online-reencrypt-v", 18))
+               return false;
+
+       ptr = flag + 18;
+       if (!*ptr)
+               return false;
+
+       while (*ptr) {
+               if (!isdigit(*ptr))
+                       return false;
+               ptr++;
+       }
+
+       return true;
+}
+
+static int hdr_validate_requirements(struct crypt_device *cd, json_object *hdr_jobj)
+{
+       int i;
+       json_object *jobj_config, *jobj, *jobj1;
+       unsigned online_reencrypt_flag = 0;
+
+       if (!(jobj_config = json_contains(cd, hdr_jobj, "", "JSON area", "config", json_type_object)))
+               return 1;
+
        /* Requirements object is optional */
        if (json_object_object_get_ex(jobj_config, "requirements", &jobj)) {
                if (!json_contains(cd, jobj_config, "section", "Config", "requirements", json_type_object))
@@ -895,12 +1057,22 @@ static int hdr_validate_config(struct crypt_device *cd, json_object *hdr_jobj)
                                return 1;
 
                        /* All array members must be strings */
-                       for (i = 0; i < (int) json_object_array_length(jobj1); i++)
+                       for (i = 0; i < (int) json_object_array_length(jobj1); i++) {
                                if (!json_object_is_type(json_object_array_get_idx(jobj1, i), json_type_string))
                                        return 1;
+
+                               if (reencrypt_candidate_flag(json_object_get_string(json_object_array_get_idx(jobj1, i))))
+                                       online_reencrypt_flag++;
+
+                       }
                }
        }
 
+       if (online_reencrypt_flag > 1) {
+               log_dbg(cd, "Multiple online reencryption requirement flags detected.");
+               return 1;
+       }
+
        return 0;
 }
 
@@ -909,6 +1081,7 @@ int LUKS2_hdr_validate(struct crypt_device *cd, json_object *hdr_jobj, uint64_t
        struct {
                int (*validate)(struct crypt_device *, json_object *);
        } checks[] = {
+               { hdr_validate_requirements },
                { hdr_validate_tokens   },
                { hdr_validate_digests  },
                { hdr_validate_segments },
@@ -936,6 +1109,33 @@ int LUKS2_hdr_validate(struct crypt_device *cd, json_object *hdr_jobj, uint64_t
        return 0;
 }
 
+static bool hdr_json_free(json_object **jobj)
+{
+       assert(jobj);
+
+       if (json_object_put(*jobj))
+               *jobj = NULL;
+
+       return (*jobj == NULL);
+}
+
+static int hdr_update_copy_for_rollback(struct crypt_device *cd, struct luks2_hdr *hdr)
+{
+       json_object **jobj_copy;
+
+       assert(hdr);
+       assert(hdr->jobj);
+
+       jobj_copy = (json_object **)&hdr->jobj_rollback;
+
+       if (!hdr_json_free(jobj_copy)) {
+               log_dbg(cd, "LUKS2 rollback metadata copy still in use");
+               return -EINVAL;
+       }
+
+       return json_object_copy(hdr->jobj, jobj_copy) ? -ENOMEM : 0;
+}
+
 /* FIXME: should we expose do_recovery parameter explicitly? */
 int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, int repair)
 {
@@ -967,6 +1167,9 @@ int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr, int repair)
        } else
                device_read_unlock(cd, crypt_metadata_device(cd));
 
+       if (!r && (r = hdr_update_copy_for_rollback(cd, hdr)))
+               log_dbg(cd, "Failed to update rollback LUKS2 metadata.");
+
        return r;
 }
 
@@ -979,18 +1182,50 @@ static int hdr_cleanup_and_validate(struct crypt_device *cd, struct luks2_hdr *h
 
 int LUKS2_hdr_write_force(struct crypt_device *cd, struct luks2_hdr *hdr)
 {
+       int r;
+
        if (hdr_cleanup_and_validate(cd, hdr))
                return -EINVAL;
 
-       return LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), false);
+       r = LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), false);
+
+       if (!r && (r = hdr_update_copy_for_rollback(cd, hdr)))
+               log_dbg(cd, "Failed to update rollback LUKS2 metadata.");
+
+       return r;
 }
 
 int LUKS2_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr)
 {
+       int r;
+
        if (hdr_cleanup_and_validate(cd, hdr))
                return -EINVAL;
 
-       return LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), true);
+       r = LUKS2_disk_hdr_write(cd, hdr, crypt_metadata_device(cd), true);
+
+       if (!r && (r = hdr_update_copy_for_rollback(cd, hdr)))
+               log_dbg(cd, "Failed to update rollback LUKS2 metadata.");
+
+       return r;
+}
+
+int LUKS2_hdr_rollback(struct crypt_device *cd, struct luks2_hdr *hdr)
+{
+       json_object **jobj_copy;
+
+       assert(hdr->jobj_rollback);
+
+       log_dbg(cd, "Rolling back in-memory LUKS2 json metadata.");
+
+       jobj_copy = (json_object **)&hdr->jobj;
+
+       if (!hdr_json_free(jobj_copy)) {
+               log_dbg(cd, "LUKS2 header still in use");
+               return -EINVAL;
+       }
+
+       return json_object_copy(hdr->jobj_rollback, jobj_copy) ? -ENOMEM : 0;
 }
 
 int LUKS2_hdr_uuid(struct crypt_device *cd, struct luks2_hdr *hdr, const char *uuid)
@@ -1027,13 +1262,22 @@ int LUKS2_hdr_labels(struct crypt_device *cd, struct luks2_hdr *hdr,
 
 void LUKS2_hdr_free(struct crypt_device *cd, struct luks2_hdr *hdr)
 {
-       if (json_object_put(hdr->jobj))
-               hdr->jobj = NULL;
-       else if (hdr->jobj)
+       json_object **jobj;
+
+       assert(hdr);
+
+       jobj = (json_object **)&hdr->jobj;
+
+       if (!hdr_json_free(jobj))
                log_dbg(cd, "LUKS2 header still in use");
+
+       jobj = (json_object **)&hdr->jobj_rollback;
+
+       if (!hdr_json_free(jobj))
+               log_dbg(cd, "LUKS2 rollback metadata copy still in use");
 }
 
-uint64_t LUKS2_keyslots_size(json_object *jobj)
+static uint64_t LUKS2_keyslots_size_jobj(json_object *jobj)
 {
        json_object *jobj1, *jobj2;
        uint64_t keyslots_size;
@@ -1045,9 +1289,19 @@ uint64_t LUKS2_keyslots_size(json_object *jobj)
        return keyslots_size;
 }
 
-uint64_t LUKS2_hdr_and_areas_size(json_object *jobj)
+uint64_t LUKS2_keyslots_size(struct luks2_hdr *hdr)
 {
-       return 2 * LUKS2_metadata_size(jobj) + LUKS2_keyslots_size(jobj);
+       return LUKS2_keyslots_size_jobj(hdr->jobj);
+}
+
+uint64_t LUKS2_hdr_and_areas_size_jobj(json_object *jobj)
+{
+       return 2 * LUKS2_metadata_size_jobj(jobj) + LUKS2_keyslots_size_jobj(jobj);
+}
+
+uint64_t LUKS2_hdr_and_areas_size(struct luks2_hdr *hdr)
+{
+       return LUKS2_hdr_and_areas_size_jobj(hdr->jobj);
 }
 
 int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr,
@@ -1059,10 +1313,10 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr,
        ssize_t ret, buffer_size;
        char *buffer = NULL;
 
-       hdr_size = LUKS2_hdr_and_areas_size(hdr->jobj);
+       hdr_size = LUKS2_hdr_and_areas_size(hdr);
        buffer_size = size_round_up(hdr_size, crypt_getpagesize());
 
-       buffer = crypt_safe_alloc(buffer_size);
+       buffer = malloc(buffer_size);
        if (!buffer)
                return -ENOMEM;
 
@@ -1073,23 +1327,22 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr,
        if (r) {
                log_err(cd, _("Failed to acquire read lock on device %s."),
                        device_path(crypt_metadata_device(cd)));
-               crypt_safe_free(buffer);
-               return r;
+               goto out;
        }
 
        devfd = device_open_locked(cd, device, O_RDONLY);
        if (devfd < 0) {
                device_read_unlock(cd, device);
                log_err(cd, _("Device %s is not a valid LUKS device."), device_path(device));
-               crypt_safe_free(buffer);
-               return devfd == -1 ? -EINVAL : devfd;
+               r = (devfd == -1) ? -EINVAL : devfd;
+               goto out;
        }
 
        if (read_lseek_blockwise(devfd, device_block_size(cd, device),
                           device_alignment(device), buffer, hdr_size, 0) < hdr_size) {
                device_read_unlock(cd, device);
-               crypt_safe_free(buffer);
-               return -EIO;
+               r = -EIO;
+               goto out;
        }
 
        device_read_unlock(cd, device);
@@ -1100,8 +1353,8 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr,
                        log_err(cd, _("Requested header backup file %s already exists."), backup_file);
                else
                        log_err(cd, _("Cannot create header backup file %s."), backup_file);
-               crypt_safe_free(buffer);
-               return -EINVAL;
+               r = -EINVAL;
+               goto out;
        }
        ret = write_buffer(fd, buffer, buffer_size);
        close(fd);
@@ -1110,26 +1363,12 @@ int LUKS2_hdr_backup(struct crypt_device *cd, struct luks2_hdr *hdr,
                r = -EIO;
        } else
                r = 0;
-
-       crypt_safe_free(buffer);
+out:
+       crypt_safe_memzero(buffer, buffer_size);
+       free(buffer);
        return r;
 }
 
-static int reqs_unknown(uint32_t reqs)
-{
-       return reqs & CRYPT_REQUIREMENT_UNKNOWN;
-}
-
-static int reqs_reencrypt(uint32_t reqs)
-{
-       return reqs & CRYPT_REQUIREMENT_OFFLINE_REENCRYPT;
-}
-
-static int reqs_reencrypt_online(uint32_t reqs)
-{
-       return reqs & CRYPT_REQUIREMENT_ONLINE_REENCRYPT;
-}
-
 int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr,
                     const char *backup_file)
 {
@@ -1137,8 +1376,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr,
        int r, fd, devfd = -1, diff_uuid = 0;
        ssize_t ret, buffer_size = 0;
        char *buffer = NULL, msg[1024];
-       struct luks2_hdr hdr_file;
-       struct luks2_hdr tmp_hdr = {};
+       struct luks2_hdr hdr_file = {}, tmp_hdr = {};
        uint32_t reqs = 0;
 
        r = device_alloc(cd, &backup_device, backup_file);
@@ -1170,8 +1408,8 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr,
                goto out;
        }
 
-       buffer_size = LUKS2_hdr_and_areas_size(hdr_file.jobj);
-       buffer = crypt_safe_alloc(buffer_size);
+       buffer_size = LUKS2_hdr_and_areas_size(&hdr_file);
+       buffer = malloc(buffer_size);
        if (!buffer) {
                r = -ENOMEM;
                goto out;
@@ -1210,7 +1448,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd, struct luks2_hdr *hdr,
                                goto out;
                        }
                        /* FIXME: what could go wrong? Erase if we're fine with consequences */
-                       if (buffer_size != (ssize_t) LUKS2_hdr_and_areas_size(tmp_hdr.jobj)) {
+                       if (buffer_size != (ssize_t) LUKS2_hdr_and_areas_size(&tmp_hdr)) {
                                log_err(cd, _("Binary header with keyslot areas size differ on device and backup, restore failed."));
                                r = -EINVAL;
                                goto out;
@@ -1271,10 +1509,9 @@ out:
        LUKS2_hdr_free(cd, &tmp_hdr);
        crypt_safe_memzero(&hdr_file, sizeof(hdr_file));
        crypt_safe_memzero(&tmp_hdr, sizeof(tmp_hdr));
-       crypt_safe_free(buffer);
-
+       crypt_safe_memzero(buffer, buffer_size);
+       free(buffer);
        device_sync(cd, device);
-
        return r;
 }
 
@@ -1289,6 +1526,8 @@ static const struct  {
        { CRYPT_ACTIVATE_SAME_CPU_CRYPT,         "same-cpu-crypt" },
        { CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS, "submit-from-crypt-cpus" },
        { CRYPT_ACTIVATE_NO_JOURNAL,             "no-journal" },
+       { CRYPT_ACTIVATE_NO_READ_WORKQUEUE,      "no-read-workqueue" },
+       { CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE,     "no-write-workqueue" },
        { 0, NULL }
 };
 
@@ -1363,24 +1602,135 @@ int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint3
  */
 
 /* LUKS2 library requirements */
-static const struct  {
+struct requirement_flag {
        uint32_t flag;
+       uint8_t version;
        const char *description;
-} requirements_flags[] = {
-       { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT, "offline-reencrypt" },
-       { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, "online-reencrypt" },
-       { 0, NULL }
 };
 
-static uint32_t get_requirement_by_name(const char *requirement)
+static const struct requirement_flag unknown_requirement_flag = { CRYPT_REQUIREMENT_UNKNOWN, 0, NULL };
+
+static const struct requirement_flag requirements_flags[] = {
+       { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT,1, "offline-reencrypt" },
+       { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 2, "online-reencrypt-v2" },
+       { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 3, "online-reencrypt-v3" },
+       { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1, "online-reencrypt" },
+       { 0, 0, NULL }
+};
+
+static const struct requirement_flag *get_requirement_by_name(const char *requirement)
 {
        int i;
 
        for (i = 0; requirements_flags[i].description; i++)
                if (!strcmp(requirement, requirements_flags[i].description))
-                       return requirements_flags[i].flag;
+                       return requirements_flags + i;
 
-       return CRYPT_REQUIREMENT_UNKNOWN;
+       return &unknown_requirement_flag;
+}
+
+static json_object *mandatory_requirements_jobj(struct luks2_hdr *hdr)
+{
+       json_object *jobj_config, *jobj_requirements, *jobj_mandatory;
+
+       assert(hdr);
+
+       if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
+               return NULL;
+
+       if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements))
+               return NULL;
+
+       if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory))
+               return NULL;
+
+       return jobj_mandatory;
+}
+
+bool LUKS2_reencrypt_requirement_candidate(struct luks2_hdr *hdr)
+{
+       json_object *jobj_mandatory;
+       int i, len;
+
+       assert(hdr);
+
+       jobj_mandatory = mandatory_requirements_jobj(hdr);
+       if (!jobj_mandatory)
+               return false;
+
+       len = (int) json_object_array_length(jobj_mandatory);
+       if (len <= 0)
+               return false;
+
+       for (i = 0; i < len; i++) {
+               if (reencrypt_candidate_flag(json_object_get_string(json_object_array_get_idx(jobj_mandatory, i))))
+                       return true;
+       }
+
+       return false;
+}
+
+int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint8_t *version)
+{
+       json_object *jobj_mandatory, *jobj;
+       int i, len;
+       const struct requirement_flag *req;
+
+       assert(hdr);
+       assert(version);
+
+       jobj_mandatory = mandatory_requirements_jobj(hdr);
+       if (!jobj_mandatory)
+               return -ENOENT;
+
+       len = (int) json_object_array_length(jobj_mandatory);
+       if (len <= 0)
+               return -ENOENT;
+
+       for (i = 0; i < len; i++) {
+               jobj = json_object_array_get_idx(jobj_mandatory, i);
+
+               /* search for requirements prefixed with "online-reencrypt" */
+               if (strncmp(json_object_get_string(jobj), "online-reencrypt", 16))
+                       continue;
+
+               /* check current library is aware of the requirement */
+               req = get_requirement_by_name(json_object_get_string(jobj));
+               if (req->flag == CRYPT_REQUIREMENT_UNKNOWN)
+                       continue;
+
+               *version = req->version;
+
+               return 0;
+       }
+
+       return -ENOENT;
+}
+
+static const struct requirement_flag *stored_requirement_name_by_id(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t req_id)
+{
+       json_object *jobj_mandatory, *jobj;
+       int i, len;
+       const struct requirement_flag *req;
+
+       assert(hdr);
+
+       jobj_mandatory = mandatory_requirements_jobj(hdr);
+       if (!jobj_mandatory)
+               return NULL;
+
+       len = (int) json_object_array_length(jobj_mandatory);
+       if (len <= 0)
+               return NULL;
+
+       for (i = 0; i < len; i++) {
+               jobj = json_object_array_get_idx(jobj_mandatory, i);
+               req = get_requirement_by_name(json_object_get_string(jobj));
+               if (req->flag == req_id)
+                       return req;
+       }
+
+       return NULL;
 }
 
 /*
@@ -1388,23 +1738,17 @@ static uint32_t get_requirement_by_name(const char *requirement)
  */
 int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs)
 {
-       json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj;
+       json_object *jobj_mandatory, *jobj;
        int i, len;
-       uint32_t req;
+       const struct requirement_flag *req;
 
        assert(hdr);
-       if (!hdr || !reqs)
-               return -EINVAL;
+       assert(reqs);
 
        *reqs = 0;
 
-       if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
-               return 0;
-
-       if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements))
-               return 0;
-
-       if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory))
+       jobj_mandatory = mandatory_requirements_jobj(hdr);
+       if (!jobj_mandatory)
                return 0;
 
        len = (int) json_object_array_length(jobj_mandatory);
@@ -1417,8 +1761,8 @@ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr
                jobj = json_object_array_get_idx(jobj_mandatory, i);
                req = get_requirement_by_name(json_object_get_string(jobj));
                log_dbg(cd, "%s - %sknown", json_object_get_string(jobj),
-                                       reqs_unknown(req) ? "un" : "");
-               *reqs |= req;
+                                       reqs_unknown(req->flag) ? "un" : "");
+               *reqs |= req->flag;
        }
 
        return 0;
@@ -1428,6 +1772,8 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr
 {
        json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj;
        int i, r = -EINVAL;
+       const struct requirement_flag *req;
+       uint32_t req_id;
 
        if (!hdr)
                return -EINVAL;
@@ -1437,8 +1783,14 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr
                return -ENOMEM;
 
        for (i = 0; requirements_flags[i].description; i++) {
-               if (reqs & requirements_flags[i].flag) {
-                       jobj = json_object_new_string(requirements_flags[i].description);
+               req_id = reqs & requirements_flags[i].flag;
+               if (req_id) {
+                       /* retain already stored version of requirement flag */
+                       req = stored_requirement_name_by_id(cd, hdr, req_id);
+                       if (req)
+                               jobj = json_object_new_string(req->description);
+                       else
+                               jobj = json_object_new_string(requirements_flags[i].description);
                        if (!jobj) {
                                r = -ENOMEM;
                                goto err;
@@ -1486,6 +1838,94 @@ err:
        return r;
 }
 
+static json_object *LUKS2_get_mandatory_requirements_filtered_jobj(struct luks2_hdr *hdr,
+       uint32_t filter_req_ids)
+{
+       int i, len;
+       const struct requirement_flag *req;
+       json_object *jobj_mandatory, *jobj_mandatory_filtered, *jobj;
+
+       jobj_mandatory_filtered = json_object_new_array();
+       if (!jobj_mandatory_filtered)
+               return NULL;
+
+       jobj_mandatory = mandatory_requirements_jobj(hdr);
+       if (!jobj_mandatory)
+               return jobj_mandatory_filtered;
+
+       len = (int) json_object_array_length(jobj_mandatory);
+
+       for (i = 0; i < len; i++) {
+               jobj = json_object_array_get_idx(jobj_mandatory, i);
+               req = get_requirement_by_name(json_object_get_string(jobj));
+               if (req->flag == CRYPT_REQUIREMENT_UNKNOWN || req->flag & filter_req_ids)
+                       continue;
+               json_object_array_add(jobj_mandatory_filtered,
+                       json_object_new_string(req->description));
+       }
+
+       return jobj_mandatory_filtered;
+}
+
+/*
+ * The function looks for specific version of requirement id.
+ * If it can't be fulfilled function fails.
+ */
+int LUKS2_config_set_requirement_version(struct crypt_device *cd,
+       struct luks2_hdr *hdr,
+       uint32_t req_id,
+       uint8_t req_version,
+       bool commit)
+{
+       json_object *jobj_config, *jobj_requirements, *jobj_mandatory;
+       const struct requirement_flag *req;
+       int r = -EINVAL;
+
+       if (!hdr || req_id == CRYPT_REQUIREMENT_UNKNOWN)
+               return -EINVAL;
+
+       req = requirements_flags;
+
+       while (req->description) {
+               /* we have a match */
+               if (req->flag == req_id && req->version == req_version)
+                       break;
+               req++;
+       }
+
+       if (!req->description)
+               return -EINVAL;
+
+       /*
+        * Creates copy of mandatory requirements set without specific requirement
+        * (no matter the version) we want to set.
+        */
+       jobj_mandatory = LUKS2_get_mandatory_requirements_filtered_jobj(hdr, req_id);
+       if (!jobj_mandatory)
+               return -ENOMEM;
+
+       json_object_array_add(jobj_mandatory, json_object_new_string(req->description));
+
+       if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config))
+               goto err;
+
+       if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) {
+               jobj_requirements = json_object_new_object();
+               if (!jobj_requirements) {
+                       r = -ENOMEM;
+                       goto err;
+               }
+               json_object_object_add(jobj_config, "requirements", jobj_requirements);
+       }
+
+       json_object_object_add(jobj_requirements, "mandatory", jobj_mandatory);
+
+       return commit ? LUKS2_hdr_write(cd, hdr) : 0;
+err:
+       json_object_put(jobj_mandatory);
+       return r;
+}
+
 /*
  * Header dump
  */
@@ -1554,7 +1994,8 @@ static void hdr_dump_keyslots(struct crypt_device *cd, json_object *hdr_jobj)
        json_object_object_get_ex(hdr_jobj, "keyslots", &keyslots_jobj);
 
        for (j = 0; j < LUKS2_KEYSLOTS_MAX; j++) {
-               (void) snprintf(slot, sizeof(slot), "%i", j);
+               if (snprintf(slot, sizeof(slot), "%i", j) < 0)
+                       slot[0] = '\0';
                json_object_object_get_ex(keyslots_jobj, slot, &val);
                if (!val)
                        continue;
@@ -1596,7 +2037,8 @@ static void hdr_dump_tokens(struct crypt_device *cd, json_object *hdr_jobj)
        json_object_object_get_ex(hdr_jobj, "tokens", &tokens_jobj);
 
        for (j = 0; j < LUKS2_TOKENS_MAX; j++) {
-               (void) snprintf(token, sizeof(token), "%i", j);
+               if (snprintf(token, sizeof(token), "%i", j) < 0)
+                       token[0] = '\0';
                json_object_object_get_ex(tokens_jobj, token, &val);
                if (!val)
                        continue;
@@ -1610,7 +2052,7 @@ static void hdr_dump_tokens(struct crypt_device *cd, json_object *hdr_jobj)
                json_object_object_get_ex(val, "keyslots", &jobj2);
                for (i = 0; i < (int) json_object_array_length(jobj2); i++) {
                        jobj3 = json_object_array_get_idx(jobj2, i);
-                       log_std(cd, "\tKeyslot:  %s\n", json_object_get_string(jobj3));
+                       log_std(cd, "\tKeyslot:    %s\n", json_object_get_string(jobj3));
                }
        }
 }
@@ -1626,7 +2068,8 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj)
        json_object_object_get_ex(hdr_jobj, "segments", &jobj_segments);
 
        for (i = 0; i < LUKS2_SEGMENT_MAX; i++) {
-               (void) snprintf(segment, sizeof(segment), "%i", i);
+               if (snprintf(segment, sizeof(segment), "%i", i) < 0)
+                       segment[0] = '\0';
                if (!json_object_object_get_ex(jobj_segments, segment, &jobj_segment))
                        continue;
 
@@ -1681,7 +2124,8 @@ static void hdr_dump_digests(struct crypt_device *cd, json_object *hdr_jobj)
        json_object_object_get_ex(hdr_jobj, "digests", &jobj1);
 
        for (i = 0; i < LUKS2_DIGEST_MAX; i++) {
-               (void) snprintf(key, sizeof(key), "%i", i);
+               if (snprintf(key, sizeof(key), "%i", i) < 0)
+                       key[0] = '\0';
                json_object_object_get_ex(jobj1, key, &val);
                if (!val)
                        continue;
@@ -1704,8 +2148,8 @@ int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr)
        log_std(cd, "LUKS header information\n");
        log_std(cd, "Version:       \t%u\n", hdr->version);
        log_std(cd, "Epoch:         \t%" PRIu64 "\n", hdr->seqid);
-       log_std(cd, "Metadata area: \t%" PRIu64 " [bytes]\n", LUKS2_metadata_size(hdr->jobj));
-       log_std(cd, "Keyslots area: \t%" PRIu64 " [bytes]\n", LUKS2_keyslots_size(hdr->jobj));
+       log_std(cd, "Metadata area: \t%" PRIu64 " [bytes]\n", LUKS2_metadata_size(hdr));
+       log_std(cd, "Keyslots area: \t%" PRIu64 " [bytes]\n", LUKS2_keyslots_size(hdr));
        log_std(cd, "UUID:          \t%s\n", *hdr->uuid ? hdr->uuid : "(no UUID)");
        log_std(cd, "Label:         \t%s\n", *hdr->label ? hdr->label : "(no label)");
        log_std(cd, "Subsystem:     \t%s\n", *hdr->subsystem ? hdr->subsystem : "(no subsystem)");
@@ -1719,23 +2163,45 @@ int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr)
        return 0;
 }
 
+int LUKS2_hdr_dump_json(struct crypt_device *cd, struct luks2_hdr *hdr, const char **json)
+{
+       const char *json_buf;
+
+       json_buf = json_object_to_json_string_ext(hdr->jobj,
+               JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE);
+
+       if (!json_buf)
+               return -EINVAL;
+
+       if (json)
+               *json = json_buf;
+       else
+               crypt_log(cd, CRYPT_LOG_NORMAL, json_buf);
+
+       return 0;
+}
+
 int LUKS2_get_data_size(struct luks2_hdr *hdr, uint64_t *size, bool *dynamic)
 {
-       int sector_size;
-       json_object *jobj_segments, *jobj_size;
+       int i, len, sector_size;
+       json_object *jobj_segments, *jobj_segment, *jobj_size;
        uint64_t tmp = 0;
 
        if (!size || !json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments))
                return -EINVAL;
 
-       json_object_object_foreach(jobj_segments, key, val) {
-               UNUSED(key);
-               if (json_segment_is_backup(val))
-                       continue;
+       len = json_object_object_length(jobj_segments);
+
+       for (i = 0; i < len; i++) {
+               if (!(jobj_segment = json_segments_get_segment(jobj_segments, i)))
+                       return -EINVAL;
+
+               if (json_segment_is_backup(jobj_segment))
+                       break;
 
-               json_object_object_get_ex(val, "size", &jobj_size);
+               json_object_object_get_ex(jobj_segment, "size", &jobj_size);
                if (!strcmp(json_object_get_string(jobj_size), "dynamic")) {
-                       sector_size = json_segment_get_sector_size(val);
+                       sector_size = json_segment_get_sector_size(jobj_segment);
                        /* last dynamic segment must have at least one sector in size */
                        if (tmp)
                                *size = tmp + (sector_size > 0 ? sector_size : SECTOR_SIZE);
@@ -1764,7 +2230,7 @@ uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr)
        crypt_reencrypt_info ri;
        json_object *jobj;
 
-       ri = LUKS2_reenc_status(hdr);
+       ri = LUKS2_reencrypt_status(hdr);
        if (ri == CRYPT_REENCRYPT_CLEAN || ri == CRYPT_REENCRYPT_CRASH) {
                jobj = LUKS2_get_segment_by_flag(hdr, "backup-final");
                if (jobj)
@@ -1792,7 +2258,7 @@ const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment)
        return json_segment_get_cipher(jobj_segment) ?: "null";
 }
 
-crypt_reencrypt_info LUKS2_reenc_status(struct luks2_hdr *hdr)
+crypt_reencrypt_info LUKS2_reencrypt_status(struct luks2_hdr *hdr)
 {
        uint32_t reqs;
 
@@ -1942,15 +2408,9 @@ int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment)
        return -1;
 }
 
-int LUKS2_get_sector_size(struct luks2_hdr *hdr)
+uint32_t LUKS2_get_sector_size(struct luks2_hdr *hdr)
 {
-       json_object *jobj_segment;
-
-       jobj_segment = LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT);
-       if (!jobj_segment)
-               return SECTOR_SIZE;
-
-       return json_segment_get_sector_size(jobj_segment) ?: SECTOR_SIZE;
+       return json_segment_get_sector_size(LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT));
 }
 
 int LUKS2_assembly_multisegment_dmd(struct crypt_device *cd,
@@ -2227,7 +2687,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
        tgt = &dmd->segment;
 
        /* TODO: We have LUKS2 dependencies now */
-       if (hdr && single_segment(dmd) && tgt->type == DM_CRYPT && crypt_get_integrity_tag_size(cd))
+       if (single_segment(dmd) && tgt->type == DM_CRYPT && tgt->u.crypt.tag_size)
                namei = device_dm_name(tgt->data_device);
 
        r = dm_device_deps(cd, name, deps_uuid_prefix, deps, ARRAY_SIZE(deps));
@@ -2235,7 +2695,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
                goto out;
 
        if (contains_reencryption_helper(deps)) {
-               r = crypt_reencrypt_lock_by_dm_uuid(cd, dmd->uuid, &reencrypt_lock);
+               r = LUKS2_reencrypt_lock_by_dm_uuid(cd, dmd->uuid, &reencrypt_lock);
                if (r) {
                        if (r == -EBUSY)
                                log_err(cd, _("Reencryption in-progress. Cannot deactivate device."));
@@ -2314,7 +2774,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
        }
 
 out:
-       crypt_reencrypt_unlock(cd, reencrypt_lock);
+       LUKS2_reencrypt_unlock(cd, reencrypt_lock);
        dep = deps;
        while (*dep)
                free(*dep++);