// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * cryptsetup LUKS2 custom mutator fuzz target
 *
 * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com>
 * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved.
 */

#include "proto_to_luks2_converter.h"
#include <iostream>

extern "C" {
#include "src/cryptsetup.h"
#include "luks2/luks2.h"
#include <err.h>
}

namespace LUKS2_proto {

std::string LUKS2ProtoConverter::string_uint64_to_string(const string_uint64 &str_u64) {
  std::ostringstream os;

  if (str_u64.negative())
    os << "-";

  if (str_u64.has_uint_num())
    os << str_u64.uint_num();
  else if (str_u64.has_string_num())
    os << str_u64.string_num();

  return os.str();
}

std::string LUKS2ProtoConverter::object_id_to_string(const object_id &oid) {
  std::ostringstream os;

  if (oid.has_int_id()) {
    os << (oid.int_id() % 33) - 16;
  } else if (oid.has_string_id()) {
    os << oid.string_id();
  }

  return os.str();
}

std::string LUKS2ProtoConverter::hash_algorithm_to_string(const hash_algorithm type) {
  switch (type) {
    case HASH_ALG_SHA1:
      return "sha1";
    case HASH_ALG_SHA256:
      return "sha256";
  }
}

std::string LUKS2ProtoConverter::keyslot_area_type_to_string(const keyslot_area_type type) {
  switch (type) {
    case KEYSLOT_AREA_TYPE_RAW:
      return "raw";
    case KEYSLOT_AREA_TYPE_NONE:
      return "none";
    case KEYSLOT_AREA_TYPE_JOURNAL:
      return "journal";
    case KEYSLOT_AREA_TYPE_CHECKSUM:
      return "checksum";
    case KEYSLOT_AREA_TYPE_DATASHIFT:
      return "datashift";
  }
}

void LUKS2ProtoConverter::generate_keyslot_area(struct json_object *jobj_area, const keyslot_area_description &keyslot_area_desc) {
  // mandatory fields
  if (keyslot_area_desc.has_type())
    json_object_object_add(jobj_area, "type", json_object_new_string(keyslot_area_type_to_string(keyslot_area_desc.type()).c_str()));
  if (keyslot_area_desc.has_offset())
    json_object_object_add(jobj_area, "offset", json_object_new_string(string_uint64_to_string(keyslot_area_desc.offset()).c_str()));
  if (keyslot_area_desc.has_size())
    json_object_object_add(jobj_area, "size", json_object_new_string(string_uint64_to_string(keyslot_area_desc.size()).c_str()));

  // raw type fields
  if (keyslot_area_desc.has_encryption())
    json_object_object_add(jobj_area, "encryption", json_object_new_string(keyslot_area_desc.encryption().c_str()));
  if (keyslot_area_desc.has_key_size())
    json_object_object_add(jobj_area, "key_size", json_object_new_int(keyslot_area_desc.key_size()));

  // checksum type fields
  if (keyslot_area_desc.has_hash())
    json_object_object_add(jobj_area, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_area_desc.hash()).c_str()));
  if (keyslot_area_desc.has_sector_size())
    json_object_object_add(jobj_area, "sector_size", json_object_new_int(keyslot_area_desc.sector_size()));

  // datashift type fields
  if (keyslot_area_desc.has_shift_size())
    json_object_object_add(jobj_area, "shift_size", json_object_new_string(string_uint64_to_string(keyslot_area_desc.shift_size()).c_str()));
}

std::string LUKS2ProtoConverter::keyslot_kdf_type_to_string(const keyslot_kdf_type type) {
  switch (type) {
    case KEYSLOT_KDF_TYPE_PBKDF2:
      return "pbkdf2";
    case KEYSLOT_KDF_TYPE_ARGON2I:
      return "argon2i";
    case KEYSLOT_KDF_TYPE_ARGON2ID:
      return "argon2id";
  }
}

void LUKS2ProtoConverter::generate_keyslot_kdf(struct json_object *jobj_kdf, const keyslot_kdf_description &keyslot_kdf_desc) {
  // mandatory fields
  if (keyslot_kdf_desc.has_type())
    json_object_object_add(jobj_kdf, "type", json_object_new_string(keyslot_kdf_type_to_string(keyslot_kdf_desc.type()).c_str()));

  if (keyslot_kdf_desc.has_salt())
    json_object_object_add(jobj_kdf, "salt", json_object_new_string(keyslot_kdf_desc.salt().c_str()));
  else
    json_object_object_add(jobj_kdf, "salt", json_object_new_string("6vz4xK7cjan92rDA5JF8O6Jk2HouV0O8DMB6GlztVk="));

  // pbkdf2 type
  if (keyslot_kdf_desc.has_hash())
    json_object_object_add(jobj_kdf, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_kdf_desc.hash()).c_str()));
  if (keyslot_kdf_desc.has_iterations())
    json_object_object_add(jobj_kdf, "iterations", json_object_new_int(keyslot_kdf_desc.iterations()));

  // argon2i and argon2id types
  if (keyslot_kdf_desc.has_time())
    json_object_object_add(jobj_kdf, "time", json_object_new_int(keyslot_kdf_desc.time()));
  if (keyslot_kdf_desc.has_memory())
    json_object_object_add(jobj_kdf, "memory", json_object_new_int(keyslot_kdf_desc.memory()));
  if (keyslot_kdf_desc.has_cpus())
    json_object_object_add(jobj_kdf, "cpus", json_object_new_int(keyslot_kdf_desc.cpus()));
}

std::string LUKS2ProtoConverter::keyslot_af_type_to_string(const keyslot_af_type type) {
  switch (type) {
    case KEYSLOT_AF_TYPE_LUKS1:
      return "luks1";
  }
}

void LUKS2ProtoConverter::generate_keyslot_af(struct json_object *jobj_af, const keyslot_af_description &keyslot_af_desc) {
  if (keyslot_af_desc.has_type())
    json_object_object_add(jobj_af, "type", json_object_new_string(keyslot_af_type_to_string(keyslot_af_desc.type()).c_str()));
  if (keyslot_af_desc.has_stripes())
    json_object_object_add(jobj_af, "stripes", json_object_new_int(keyslot_af_desc.stripes()));
  if (keyslot_af_desc.has_hash())
    json_object_object_add(jobj_af, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_af_desc.hash()).c_str()));
}

std::string LUKS2ProtoConverter::keyslot_type_to_string(const keyslot_type type) {
  switch (type) {
    case KEYSLOT_TYPE_LUKS2:
      return "luks2";
    case KEYSLOT_TYPE_REENCRYPT:
      return "reencrypt";
    case KEYSLOT_TYPE_PLACEHOLDER:
      return "placeholder";
  }
}

std::string LUKS2ProtoConverter::reencrypt_keyslot_mode_to_string(const reencrypt_keyslot_mode mode) {
  switch (mode) {
    case MODE_REENCRYPT:
      return "reencrypt";
    case MODE_ENCRYPT:
      return "encrypt";
    case MODE_DECRYPT:
      return "decrypt";
  }
}

std::string LUKS2ProtoConverter::reencrypt_keyslot_direction_to_string(const reencrypt_keyslot_direction direction) {
  switch (direction) {
    case DIRECTION_FORWARD:
      return "forward";
    case DIRECTION_BACKWARD:
      return "backward";
  }
}

void LUKS2ProtoConverter::generate_keyslot(struct json_object *jobj_keyslots, const keyslot_description &keyslot_desc) {
  struct json_object *jobj_keyslot, *jobj_area, *jobj_kdf, *jobj_af;

  jobj_keyslot = json_object_new_object();
  if (keyslot_desc.has_type())
    json_object_object_add(jobj_keyslot, "type", json_object_new_string(keyslot_type_to_string(keyslot_desc.type()).c_str()));
  if (keyslot_desc.has_key_size())
    json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(keyslot_desc.key_size()));
  if (keyslot_desc.has_priority())
    json_object_object_add(jobj_keyslot, "priority", json_object_new_int(keyslot_desc.priority()));
  if (keyslot_desc.has_mode())
    json_object_object_add(jobj_keyslot, "mode", json_object_new_int(keyslot_desc.mode()));
  if (keyslot_desc.has_direction())
    json_object_object_add(jobj_keyslot, "direction", json_object_new_int(keyslot_desc.direction()));

  /* Area object */
  if (keyslot_desc.has_area()) {
    jobj_area = json_object_new_object();
    generate_keyslot_area(jobj_area, keyslot_desc.area());
    json_object_object_add(jobj_keyslot, "area", jobj_area);
  }

  /* KDF object */
  if (keyslot_desc.has_kdf()) {
    jobj_kdf = json_object_new_object();
    generate_keyslot_kdf(jobj_kdf, keyslot_desc.kdf());
    json_object_object_add(jobj_keyslot, "kdf", jobj_kdf);
  }

  /* AF object */
  if (keyslot_desc.has_af()) {
    jobj_af = json_object_new_object();
    generate_keyslot_af(jobj_af, keyslot_desc.af());
    json_object_object_add(jobj_keyslot, "af", jobj_af);
  }

  json_object_object_add(jobj_keyslots, object_id_to_string(keyslot_desc.oid()).c_str(), jobj_keyslot);
}

void LUKS2ProtoConverter::generate_token(struct json_object *jobj_tokens, const token_description &token_desc) {
  struct json_object *jobj_token, *jobj_keyslots;
  jobj_token = json_object_new_object();

  if (token_desc.has_type())
    json_object_object_add(jobj_token, "type", json_object_new_string(token_desc.type().c_str()));

  if (token_desc.has_key_description())
    json_object_object_add(jobj_token, "key_description", json_object_new_string(token_desc.key_description().c_str()));

  if (!token_desc.keyslots().empty()) {
    jobj_keyslots = json_object_new_array();

    for (const object_id& oid : token_desc.keyslots()) {
        json_object_array_add(jobj_keyslots,
            json_object_new_string(object_id_to_string(oid).c_str()));
    }

    /* Replace or add new keyslots array */
    json_object_object_add(jobj_token, "keyslots", jobj_keyslots);
  }

  json_object_object_add(jobj_tokens, object_id_to_string(token_desc.oid()).c_str(), jobj_token);
}

void LUKS2ProtoConverter::generate_digest(struct json_object *jobj_digests, const digest_description &digest_desc) {
  struct json_object *jobj_digest, *jobj_keyslots, *jobj_segments;

  jobj_digest = json_object_new_object();

  if (digest_desc.has_type())
    json_object_object_add(jobj_digest, "type", json_object_new_string(keyslot_kdf_type_to_string(digest_desc.type()).c_str()));

  if (!digest_desc.keyslots().empty()) {
    jobj_keyslots = json_object_new_array();

    for (const object_id& oid : digest_desc.keyslots()) {
        json_object_array_add(jobj_keyslots,
            json_object_new_string(object_id_to_string(oid).c_str()));
    }

    /* Replace or add new keyslots array */
    json_object_object_add(jobj_digest, "keyslots", jobj_keyslots);
  }

  if (!digest_desc.segments().empty()) {
    jobj_segments = json_object_new_array();

    for (const object_id& oid : digest_desc.segments()) {
        json_object_array_add(jobj_segments,
            json_object_new_string(object_id_to_string(oid).c_str()));
    }

    /* Replace or add new segments array */
    json_object_object_add(jobj_digest, "segments", jobj_segments);
  }

  if (digest_desc.has_salt())
    json_object_object_add(jobj_digest, "salt", json_object_new_string(digest_desc.salt().c_str()));
  if (digest_desc.has_digest())
    json_object_object_add(jobj_digest, "digest", json_object_new_string(digest_desc.digest().c_str()));
  if (digest_desc.has_hash())
    json_object_object_add(jobj_digest, "hash", json_object_new_string(hash_algorithm_to_string(digest_desc.hash()).c_str()));
  if (digest_desc.has_iterations())
    json_object_object_add(jobj_digest, "iterations", json_object_new_int(digest_desc.iterations()));

  json_object_object_add(jobj_digests, object_id_to_string(digest_desc.oid()).c_str(), jobj_digest);
}

std::string LUKS2ProtoConverter::segment_type_to_string(segment_type type) {
  switch (type) {
    case SEGMENT_TYPE_LINEAR:
      return "linear";
    case SEGMENT_TYPE_CRYPT:
      return "crypt";
  }
}

std::string LUKS2ProtoConverter::segment_flag_to_string(segment_flag flag) {
  switch (flag) {
    case IN_REENCRYPTION:
      return "in-reencryption";
    case BACKUP_FINAL:
      return "backup-final";
    case BACKUP_PREVIOUS:
      return "backup-previous";
    case BACKUP_MOVED_SEGMENT:
      return "backup-moved-segment";
  }
}

void LUKS2ProtoConverter::generate_segment_integrity(struct json_object *jobj_integrity, const segment_integrity_description &segment_integrity_desc) {
  if (segment_integrity_desc.has_type())
    json_object_object_add(jobj_integrity, "type", json_object_new_string(segment_integrity_desc.type().c_str()));
  if (segment_integrity_desc.has_journal_encryption())
    json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string(segment_integrity_desc.journal_encryption().c_str()));
  if (segment_integrity_desc.has_journal_integrity())
    json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string(segment_integrity_desc.journal_integrity().c_str()));
}

void LUKS2ProtoConverter::generate_segment(struct json_object *jobj_segments, const segment_description &segment_desc) {
  json_object *jobj_flags, *jobj_integrity;
  json_object *jobj_segment = json_object_new_object();

  if (segment_desc.has_type())
    json_object_object_add(jobj_segment, "type", json_object_new_string(segment_type_to_string(segment_desc.type()).c_str()));

  if (segment_desc.has_offset())
    json_object_object_add(jobj_segment, "offset", json_object_new_string(string_uint64_to_string(segment_desc.offset()).c_str()));
  if (segment_desc.has_size())
    json_object_object_add(jobj_segment, "size", json_object_new_string(string_uint64_to_string(segment_desc.size()).c_str()));

  if (!segment_desc.flags().empty()) {
    jobj_flags = json_object_new_array();

    for (const int flag : segment_desc.flags()) {
        json_object_array_add(jobj_flags,
            json_object_new_string(segment_flag_to_string(segment_flag(flag)).c_str()));
    }

    /* Replace or add new flags array */
    json_object_object_add(jobj_segment, "flags", jobj_flags);
  }

  if (segment_desc.has_iv_tweak())
    json_object_object_add(jobj_segment, "iv_tweak", json_object_new_string(string_uint64_to_string(segment_desc.iv_tweak()).c_str()));
  if (segment_desc.has_encryption())
    json_object_object_add(jobj_segment, "encryption", json_object_new_string(segment_desc.encryption().c_str()));
  if (segment_desc.has_sector_size())
    json_object_object_add(jobj_segment, "sector_size", json_object_new_int(segment_desc.sector_size()));

  if (segment_desc.has_integrity()) {
    jobj_integrity = json_object_new_object();
    generate_segment_integrity(jobj_integrity, segment_desc.integrity());
    json_object_object_add(jobj_segment, "integrity", jobj_integrity);
  }

  json_object_object_add(jobj_segments, object_id_to_string(segment_desc.oid()).c_str(), jobj_segment);
}

void LUKS2ProtoConverter::create_jobj(const LUKS2_both_headers &headers) {
  json_object *jobj_keyslots = NULL;
  json_object *jobj_digests = NULL;
  json_object *jobj_segments = NULL;
  json_object *jobj_tokens = NULL;

  const json_area_description &json_desc = headers.json_area();

  jobj = json_object_new_object();
  if (!jobj)
    return;

  jobj_keyslots = json_object_new_object();
  for (const keyslot_description &keyslot_desc : json_desc.keyslots()) {
    generate_keyslot(jobj_keyslots, keyslot_desc);
  }
  json_object_object_add(jobj, "keyslots", jobj_keyslots);

  jobj_digests = json_object_new_object();
  for (const digest_description &digest_desc : json_desc.digests()) {
    generate_digest(jobj_digests, digest_desc);
  }
  json_object_object_add(jobj, "digests", jobj_digests);

  jobj_segments = json_object_new_object();
  for (const segment_description &segment_desc : json_desc.segments()) {
    generate_segment(jobj_segments, segment_desc);
  }
  json_object_object_add(jobj, "segments", jobj_segments);

  jobj_tokens = json_object_new_object();
  for (const token_description &token_desc : json_desc.tokens()) {
    generate_token(jobj_tokens, token_desc);
  }
  json_object_object_add(jobj, "tokens", jobj_tokens);

  if (json_desc.has_config()) {
    uint64_t hdr_size = json_desc.config().use_primary_hdr_size() ? headers.primary_header().hdr_size() : headers.secondary_header().hdr_size();
    generate_config(json_desc.config(), hdr_size - LUKS2_HDR_BIN_LEN, KEYSLOTS_SIZE);
  }
}

void LUKS2ProtoConverter::emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid) {
  struct luks2_hdr_disk hdr = {};
  int r;

  if (hd)
    crypt_hash_destroy(hd);
  if (crypt_hash_init(&hd, "sha256"))
    err(EXIT_FAILURE, "crypt_hash_init failed");


  r = lseek(fd, offset, SEEK_SET);
  if (r == -1)
    err(EXIT_FAILURE, "lseek failed");

  switch (header_proto.magic()) {
    case INVALID:
      memset(&hdr.magic, 0, LUKS2_MAGIC_L);
      break;
    case FIRST:
      memcpy(&hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
      break;
    case SECOND:
      memcpy(&hdr.magic, LUKS2_MAGIC_2ND, LUKS2_MAGIC_L);
      break;
  }
  hdr.version     = cpu_to_be16(header_proto.version());
  hdr.hdr_size    = cpu_to_be64(header_proto.hdr_size());
  hdr.seqid       = cpu_to_be64(seqid);
  strncpy(hdr.checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
  hdr.checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
  strncpy(hdr.uuid, "af7f64ea-3233-4581-946b-6187d812841e", LUKS2_UUID_L);
  memset(hdr.salt, 1, LUKS2_SALT_L);


  if (header_proto.has_selected_offset())
    hdr.hdr_offset  = cpu_to_be64(header_proto.selected_offset());
  else
    hdr.hdr_offset  = cpu_to_be64(offset);

  if (write_buffer(fd, &hdr, LUKS2_HDR_BIN_LEN) != LUKS2_HDR_BIN_LEN)
    err(EXIT_FAILURE, "write_buffer failed");
  if (crypt_hash_write(hd, (char*)&hdr, LUKS2_HDR_BIN_LEN))
    err(EXIT_FAILURE, "crypt_hash_write failed");

  size_t hdr_json_area_len = header_proto.hdr_size() - LUKS2_HDR_BIN_LEN;
  size_t json_text_len;
  const char *json_text;
  uint8_t csum[LUKS2_CHECKSUM_L];

  if (jobj) {
    json_text = json_object_to_json_string_ext((struct json_object *)jobj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
    if (!json_text || !*json_text)
      err(EXIT_FAILURE, "json_object_to_json_string_ext failed");

    json_text_len = strlen(json_text);

    size_t write_size = json_text_len > hdr_json_area_len - 1 ? hdr_json_area_len - 1 : json_text_len;
    if (write_buffer(fd, json_text, write_size) != (ssize_t)write_size)
      err(EXIT_FAILURE, "write_buffer failed");
    if (crypt_hash_write(hd, json_text, write_size))
      err(EXIT_FAILURE, "crypt_hash_write failed");

    for (size_t i = 0; i < (hdr_json_area_len - write_size); i++) {
      if (crypt_hash_write(hd, "\0", 1))
        err(EXIT_FAILURE, "crypt_hash_write failed");
    }
  }

  if (header_proto.use_correct_checksum()) {
    if (lseek(fd, offset + offsetof(luks2_hdr_disk, csum), SEEK_SET) == -1)
      err(EXIT_FAILURE, "lseek failed");

    int hash_size = crypt_hash_size("sha256");
    if (hash_size <= 0)
      err(EXIT_FAILURE, "crypt_hash_size failed");

    if (crypt_hash_final(hd, (char*)csum, (size_t)hash_size))
      err(EXIT_FAILURE, "crypt_hash_final failed");
    if (write_buffer(fd, csum, hash_size) != hash_size)
      err(EXIT_FAILURE, "write_buffer failed");
  }
}

void LUKS2ProtoConverter::set_write_headers_only(bool headers_only) {
  write_headers_only = headers_only;
}

void LUKS2ProtoConverter::convert(const LUKS2_both_headers &headers, int fd) {
  uint64_t primary_seqid, secondary_seqid;
  int result;

  size_t out_size = headers.primary_header().hdr_size() + headers.secondary_header().hdr_size();

  if (!write_headers_only)
    out_size += KEYSLOTS_SIZE + DATA_SIZE;

  result = ftruncate(fd, out_size);
  if (result == -1)
    err(EXIT_FAILURE, "truncate failed");

  result = lseek(fd, 0, SEEK_SET);
  if (result == -1)
    err(EXIT_FAILURE, "lseek failed");

  switch (headers.seqid()) {
    case EQUAL:
      primary_seqid = 1;
      secondary_seqid = 1;
      break;
    case PRIMARY_GREATER:
      primary_seqid = 2;
      secondary_seqid = 1;
      break;
    case SECONDARY_GREATER:
      primary_seqid = 1;
      secondary_seqid = 2;
      break;
  }

  create_jobj(headers);
  emit_luks2_binary_header(headers.primary_header(), fd, 0, primary_seqid);
  emit_luks2_binary_header(headers.secondary_header(), fd, headers.primary_header().hdr_size(), secondary_seqid);
}

std::string LUKS2ProtoConverter::config_flag_to_string(config_flag flag) {
  switch (flag) {
    case CONFIG_FLAG_ALLOW_DISCARDS:
      return "allow-discards";
    case CONFIG_FLAG_SAME_CPU_CRYPT:
      return "same-cpu-crypt";
    case CONFIG_FLAG_SUBMIT_FROM_CRYPT_CPUS:
      return "submit-from-crypt-cpus";
    case CONFIG_FLAG_NO_JOURNAL:
      return "no-journal";
    case CONFIG_FLAG_NO_READ_WORKQUEUE:
      return "no-read-workqueue";
    case CONFIG_FLAG_NO_WRITE_WORKQUEUE:
      return "no-write-workqueue";
  }
}

std::string LUKS2ProtoConverter::config_requirement_to_string(config_requirement requirement) {
  switch (requirement) {
    case CONFIG_REQUIREMENT_OFFLINE_REENCRYPT:
      return "offline-reencrypt";
    case CONFIG_REQUIREMENT_ONLINE_REENCRYPT_V2:
      return "online-reencrypt-v2";
  }
}

void LUKS2ProtoConverter::generate_config(const config_description &config_desc, uint64_t json_size, uint64_t keyslots_size) {
  json_object *jobj_config, *jobj_flags, *jobj_requirements, *jobj_mandatory;
  jobj_config = json_object_new_object();

  json_object_object_add(jobj_config, "json_size", json_object_new_string(std::to_string(json_size).c_str()));
  json_object_object_add(jobj_config, "keyslots_size", json_object_new_string(std::to_string(keyslots_size).c_str()));

  if (!config_desc.config_flags().empty()) {
    jobj_flags = json_object_new_array();

    for (const int flag : config_desc.config_flags()) {
        json_object_array_add(jobj_flags,
            json_object_new_string(config_flag_to_string(config_flag(flag)).c_str()));
    }

    /* Replace or add new flags array */
    json_object_object_add(jobj_config, "flags", jobj_flags);
  }

  if (!config_desc.requirements().empty()) {
    jobj_requirements = json_object_new_object();
    jobj_mandatory = json_object_new_array();

    for (const int requirement : config_desc.requirements()) {
        json_object_array_add(jobj_mandatory,
            json_object_new_string(config_requirement_to_string(config_requirement(requirement)).c_str()));
    }

    /* Replace or add new requirements array */
    json_object_object_add(jobj_requirements, "mandatory", jobj_mandatory);
    json_object_object_add(jobj_config, "requirements", jobj_requirements);
  }

  json_object_object_add(jobj, "config", jobj_config);
}

LUKS2ProtoConverter::~LUKS2ProtoConverter() {
  json_object_put(jobj);
  if (hd)
    crypt_hash_destroy(hd);
}
}  // namespace LUKS2_proto
