/*
 * LUKS - Linux Unified Key Setup v2
 *
 * Copyright (C) 2015-2021 Red Hat, Inc. All rights reserved.
 * Copyright (C) 2015-2021 Milan Broz
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef _CRYPTSETUP_LUKS2_INTERNAL_H
#define _CRYPTSETUP_LUKS2_INTERNAL_H

#include <stdio.h>
#include <errno.h>
#include <json-c/json.h>

#include "internal.h"
#include "base64.h"
#include "luks2.h"

#define UNUSED(x) (void)(x)

/* override useless forward slash escape when supported by json-c */
#ifndef JSON_C_TO_STRING_NOSLASHESCAPE
#define JSON_C_TO_STRING_NOSLASHESCAPE 0
#endif

/*
 * On-disk access function prototypes
 */
int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
			struct device *device, int do_recovery, int do_blkprobe);
int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr,
			 struct device *device, bool seqid_check);
int LUKS2_device_write_lock(struct crypt_device *cd,
	struct luks2_hdr *hdr, struct device *device);

/*
 * JSON struct access helpers
 */
json_object *LUKS2_get_keyslot_jobj(struct luks2_hdr *hdr, int keyslot);
json_object *LUKS2_get_token_jobj(struct luks2_hdr *hdr, int token);
json_object *LUKS2_get_digest_jobj(struct luks2_hdr *hdr, int digest);
json_object *LUKS2_get_segment_jobj(struct luks2_hdr *hdr, int segment);
json_object *LUKS2_get_tokens_jobj(struct luks2_hdr *hdr);
json_object *LUKS2_get_segments_jobj(struct luks2_hdr *hdr);

void hexprint_base64(struct crypt_device *cd, json_object *jobj,
		     const char *sep, const char *line_sep);

uint64_t crypt_jobj_get_uint64(json_object *jobj);
uint32_t crypt_jobj_get_uint32(json_object *jobj);
json_object *crypt_jobj_new_uint64(uint64_t value);

int json_object_object_add_by_uint(json_object *jobj, unsigned key, json_object *jobj_val);
void json_object_object_del_by_uint(json_object *jobj, unsigned key);
int json_object_copy(json_object *jobj_src, json_object **jobj_dst);

void JSON_DBG(struct crypt_device *cd, json_object *jobj, const char *desc);

/*
 * LUKS2 JSON validation
 */

/* validation helper */
json_bool validate_json_uint32(json_object *jobj);
json_object *json_contains(struct crypt_device *cd, json_object *jobj, const char *name,
			   const char *section, const char *key, json_type type);

int LUKS2_hdr_validate(struct crypt_device *cd, json_object *hdr_jobj, uint64_t json_size);
int LUKS2_check_json_size(struct crypt_device *cd, const struct luks2_hdr *hdr);
int LUKS2_token_validate(struct crypt_device *cd, json_object *hdr_jobj,
			 json_object *jobj_token, const char *key);
void LUKS2_token_dump(struct crypt_device *cd, int token);

/*
 * LUKS2 JSON repair for known glitches
 */
void LUKS2_hdr_repair(struct crypt_device *cd, json_object *jobj_hdr);
void LUKS2_keyslots_repair(struct crypt_device *cd, json_object *jobj_hdr);

/*
 * JSON array helpers
 */
json_object *LUKS2_array_jobj(json_object *array, const char *num);
json_object *LUKS2_array_remove(json_object *array, const char *num);

/*
 * Plugins API
 */

/**
 * LUKS2 keyslots handlers (EXPERIMENTAL)
 */
typedef int (*keyslot_alloc_func)(struct crypt_device *cd, int keyslot,
				  size_t volume_key_len,
				  const struct luks2_keyslot_params *params);
typedef int (*keyslot_update_func)(struct crypt_device *cd, int keyslot,
				   const struct luks2_keyslot_params *params);
typedef int (*keyslot_open_func) (struct crypt_device *cd, int keyslot,
				  const char *password, size_t password_len,
				  char *volume_key, size_t volume_key_len);
typedef int (*keyslot_store_func)(struct crypt_device *cd, int keyslot,
				  const char *password, size_t password_len,
				  const char *volume_key, size_t volume_key_len);
typedef int (*keyslot_wipe_func) (struct crypt_device *cd, int keyslot);
typedef int (*keyslot_dump_func) (struct crypt_device *cd, int keyslot);
typedef int (*keyslot_validate_func) (struct crypt_device *cd, json_object *jobj_keyslot);
typedef void(*keyslot_repair_func) (struct crypt_device *cd, json_object *jobj_keyslot);

/* see LUKS2_luks2_to_luks1 */
int placeholder_keyslot_alloc(struct crypt_device *cd,
	int keyslot,
	uint64_t area_offset,
	uint64_t area_length,
	size_t volume_key_len);

/* validate all keyslot implementations in hdr json */
int LUKS2_keyslots_validate(struct crypt_device *cd, json_object *hdr_jobj);

typedef struct  {
	const char *name;
	keyslot_alloc_func alloc;
	keyslot_update_func update;
	keyslot_open_func  open;
	keyslot_store_func store;
	keyslot_wipe_func  wipe;
	keyslot_dump_func  dump;
	keyslot_validate_func validate;
	keyslot_repair_func repair;
} keyslot_handler;

/* can not fit prototype alloc function */
int reenc_keyslot_alloc(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	int keyslot,
	const struct crypt_params_reencrypt *params);

/**
 * LUKS2 digest handlers (EXPERIMENTAL)
 */
typedef int (*digest_verify_func)(struct crypt_device *cd, int digest,
				  const char *volume_key, size_t volume_key_len);
typedef int (*digest_store_func) (struct crypt_device *cd, int digest,
				  const char *volume_key, size_t volume_key_len);
typedef int (*digest_dump_func)  (struct crypt_device *cd, int digest);

typedef struct  {
	const char *name;
	digest_verify_func verify;
	digest_store_func  store;
	digest_dump_func   dump;
} digest_handler;

/**
 * LUKS2 token handlers (internal use only)
 */
typedef int (*builtin_token_get_func) (json_object *jobj_token, void *params);
typedef int (*builtin_token_set_func) (json_object **jobj_token, const void *params);

typedef struct {
	/* internal only section used by builtin tokens */
	builtin_token_get_func get;
	builtin_token_set_func set;
	/* public token handler */
	const crypt_token_handler *h;
} token_handler;

int token_keyring_set(json_object **, const void *);
int token_keyring_get(json_object *, void *);

int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
			size_t keylength, uint64_t *area_offset, uint64_t *area_length);
int LUKS2_find_area_max_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
			    uint64_t *area_offset, uint64_t *area_length);

uint64_t LUKS2_hdr_and_areas_size_jobj(json_object *jobj);

int LUKS2_check_cipher(struct crypt_device *cd,
		      size_t keylength,
		      const char *cipher,
		      const char *cipher_mode);

static inline const char *crypt_reencrypt_mode_to_str(crypt_reencrypt_mode_info mi)
{
	if (mi == CRYPT_REENCRYPT_REENCRYPT)
		return "reencrypt";
	if (mi == CRYPT_REENCRYPT_ENCRYPT)
		return "encrypt";
	if (mi == CRYPT_REENCRYPT_DECRYPT)
		return "decrypt";
	return "<unknown>";
}

/*
 * Generic LUKS2 keyslot
 */
int LUKS2_keyslot_reencrypt_store(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	int keyslot,
	const void *buffer,
	size_t buffer_length);

int LUKS2_keyslot_reencrypt_allocate(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	int keyslot,
	const struct crypt_params_reencrypt *params);

int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	struct volume_key *vks);

int LUKS2_keyslot_dump(struct crypt_device *cd,
	int keyslot);

int LUKS2_keyslot_jobj_area(json_object *jobj_keyslot, uint64_t *offset, uint64_t *length);

/* JSON helpers */
uint64_t json_segment_get_offset(json_object *jobj_segment, unsigned blockwise);
const char *json_segment_type(json_object *jobj_segment);
uint64_t json_segment_get_iv_offset(json_object *jobj_segment);
uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise);
const char *json_segment_get_cipher(json_object *jobj_segment);
int json_segment_get_sector_size(json_object *jobj_segment);
bool json_segment_is_backup(json_object *jobj_segment);
json_object *json_segments_get_segment(json_object *jobj_segments, int segment);
unsigned json_segments_count(json_object *jobj_segments);
void json_segment_remove_flag(json_object *jobj_segment, const char *flag);
uint64_t json_segments_get_minimal_offset(json_object *jobj_segments, unsigned blockwise);
json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length, unsigned reencryption);
json_object *json_segment_create_crypt(uint64_t offset, uint64_t iv_offset, const uint64_t *length, const char *cipher, uint32_t sector_size, unsigned reencryption);
int json_segments_segment_in_reencrypt(json_object *jobj_segments);
bool json_segment_cmp(json_object *jobj_segment_1, json_object *jobj_segment_2);
bool json_segment_contains_flag(json_object *jobj_segment, const char *flag_str, size_t len);

int LUKS2_assembly_multisegment_dmd(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	struct volume_key *vks,
	json_object *jobj_segments,
	struct crypt_dm_active_device *dmd);

/*
 * Generic LUKS2 segment
 */
int LUKS2_segments_count(struct luks2_hdr *hdr);

int LUKS2_segment_first_unused_id(struct luks2_hdr *hdr);

int LUKS2_segment_set_flag(json_object *jobj_segment, const char *flag);

json_object *LUKS2_get_segment_by_flag(struct luks2_hdr *hdr, const char *flag);

int LUKS2_get_segment_id_by_flag(struct luks2_hdr *hdr, const char *flag);

int LUKS2_segments_set(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	json_object *jobj_segments,
	int commit);

uint64_t LUKS2_segment_offset(struct luks2_hdr *hdr,
	int segment,
	unsigned blockwise);

uint64_t LUKS2_segment_size(struct luks2_hdr *hdr,
	int segment,
	unsigned blockwise);

int LUKS2_segment_is_type(struct luks2_hdr *hdr,
	int segment,
	const char *type);

int LUKS2_segment_by_type(struct luks2_hdr *hdr,
	const char *type);

int LUKS2_last_segment_by_type(struct luks2_hdr *hdr,
	const char *type);

int LUKS2_get_default_segment(struct luks2_hdr *hdr);

int LUKS2_reencrypt_digest_new(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr);
int LUKS2_reencrypt_data_offset(struct luks2_hdr *hdr, bool blockwise);

/*
 * Generic LUKS2 digest
 */
int LUKS2_digest_verify_by_digest(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	int digest,
	const struct volume_key *vk);

void LUKS2_digests_erase_unused(struct crypt_device *cd,
	struct luks2_hdr *hdr);

int LUKS2_digest_dump(struct crypt_device *cd,
	int digest);

/*
 * Generic LUKS2 token
 */
int LUKS2_tokens_count(struct luks2_hdr *hdr);

/*
 * LUKS2 generic
 */
int LUKS2_reload(struct crypt_device *cd,
	const char *name,
	struct volume_key *vks,
	uint64_t device_size,
	uint32_t flags);

int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment);
int LUKS2_find_keyslot(struct luks2_hdr *hdr, const char *type);
int LUKS2_set_keyslots_size(struct crypt_device *cd,
	struct luks2_hdr *hdr,
	uint64_t data_offset);

#endif
