/*
 * tsig.h -- TSIG definitions (RFC 2845).
 *
 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
 *
 * See LICENSE for the license.
 *
 */

#ifndef _TSIG_H_
#define _TSIG_H_

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include "buffer.h"
#include "dname.h"

#define TSIG_ERROR_NOERROR  0
#define TSIG_ERROR_BADSIG   16
#define TSIG_ERROR_BADKEY   17
#define TSIG_ERROR_BADTIME  18

typedef struct tsig_algorithm tsig_algorithm_type;
typedef struct tsig_key tsig_key_type;
typedef struct tsig_record tsig_record_type;

enum tsig_status
{
	TSIG_NOT_PRESENT,
	TSIG_OK,
	TSIG_ERROR
};
typedef enum tsig_status tsig_status_type;

struct tsig_lookup_struct_table
{
	uint8_t id;
	const char* short_name;
};
typedef struct tsig_lookup_struct_table tsig_lookup_algorithm_table;

/*
 * A TSIG HMAC algorithm, such as hmac-md5.
 */
struct tsig_algorithm
{
	/*
	 * Short name of the algorithm, such as "hmac-md5".
	 */
	const char *short_name;

	/*
	 * Full wireformat name of the algorithm, such as
	 * "hmac-md5.sig-alg.reg.int."
	 */
	const dname_type *wireformat_name;

	/*
	 * The maximum size of a digest generated by this algorithm.
	 */
	size_t maximum_digest_size;

	/*
	 * Algorithm implementation specific data.
	 */
	const void *data;

	/*
	 * Create a new HMAC context.
	 */
	void *(*hmac_create_context)(region_type *region);

	/*
	 * Initialize an HMAC context with the specified algorithm and
	 * key.
	 */
	void  (*hmac_init_context)(void *context,
				   tsig_algorithm_type *algorithm,
				   tsig_key_type *key);

	/*
	 * Update the HMAC context with the specified data.
	 */
	void  (*hmac_update)(void *context, const void *data, size_t size);

	/*
	 * Generate the final digest.  DIGEST points to a buffer of at
	 * least maximum_digest_size bytes.
	 */
	void  (*hmac_final)(void *context, uint8_t *digest, size_t *size);
};

/*
 * A TSIG key used to sign and verify packets.
 */
struct tsig_key
{
	const dname_type *name;
	size_t            size;
	uint8_t		 *data;
};

struct tsig_record
{
	tsig_status_type     status;
	size_t               position;
	size_t               response_count;
	size_t               updates_since_last_prepare;
	void                *context;
	tsig_algorithm_type *algorithm;
	tsig_key_type       *key;
	size_t               prior_mac_size;
	uint8_t             *prior_mac_data;

	/* TSIG RR data is allocated in the rr_region.  */
	region_type      *rr_region;
	region_type	 *context_region;
	const dname_type *key_name;
	const dname_type *algorithm_name;
	uint16_t          signed_time_high;
	uint32_t          signed_time_low;
	uint16_t          signed_time_fudge;
	uint16_t          mac_size;
	uint8_t          *mac_data;
	uint16_t          original_query_id;
	uint16_t          error_code;
	uint16_t          other_size;
	uint8_t          *other_data;
};

/*
 * Initialize the TSIG module (including TSIG implementation modules
 * such as tsig-openssl).
 */
int tsig_init(region_type *region);

/*
 * Add the specified key to the TSIG key table.
 */
void tsig_add_key(tsig_key_type *key);
void tsig_del_key(tsig_key_type *key);

/*
 * Add the specified algorithm to the TSIG algorithm table.
 */
void tsig_add_algorithm(tsig_algorithm_type *algorithm);

/*
 * Find an HMAC algorithm based on its short name.
 */
tsig_algorithm_type *tsig_get_algorithm_by_name(const char *name);

/*
 * Return a descriptive error message based on the TSIG error code.
 */
const char *tsig_error(int error_code);

/*
 * Create the tsig record internal structure. Allocs it.
 * Call init_record afterwards before doing more with it.
 *
 * The region is used to attach a cleanup function that destroys the tsig.
 */
void tsig_create_record(tsig_record_type* tsig,
			region_type* region);

/*
 * Like tsig_create_record, with custom region settings.
 * The size params are used to customise the rr_region and context_region.
 * If region is NULL, no cleanup is attached to it.
 */
void tsig_create_record_custom(tsig_record_type* tsig,
			region_type* region,
			size_t chunk_size,
			size_t large_object_size,
			size_t initial_cleanup_size);

/*
 * Destroy tsig record internals (the main ptr is user alloced).
 * if region is nonNULL, removes cleanup.
 */
void tsig_delete_record(tsig_record_type* tsig, region_type* region);

/*
 * Call this before starting to analyze or signing a sequence of
 * packets.
 *
 * ALGORITHM and KEY are optional and are only needed if you want to
 * sign the initial query.  Otherwise the key and algorithm are looked
 * up in the algorithm and key table when a received TSIG RR is
 * processed.
 */
void tsig_init_record(tsig_record_type *data,
		      tsig_algorithm_type *algorithm,
		      tsig_key_type *key);

/*
 * Validate the TSIG RR key and algorithm from the TSIG RR.  Otherwise
 * update the TSIG error code.  The MAC itself is not validated.
 *
 * Returns non-zero if the key and algorithm could be validated.
 */
int tsig_from_query(tsig_record_type *tsig);

/*
 * Prepare TSIG for signing of a query.  This initializes TSIG with
 * the algorithm and key stored in the TSIG record.
 */
void tsig_init_query(tsig_record_type *tsig, uint16_t original_query_id);

/*
 * Prepare TSIG for performing an HMAC calculation.  If the TSIG
 * contains a prior HMAC it is inserted first into the hash
 * calculation.
 */
void tsig_prepare(tsig_record_type *tsig);

/*
 * Add the first LENGTH octets of PACKET to the TSIG hash, replacing
 * the PACKET's id with the original query id from TSIG.  If the query
 * is a response the TSIG response count is incremented.
 */
void tsig_update(tsig_record_type *tsig, buffer_type *packet, size_t length);

/*
 * Finalize the TSIG record by hashing the TSIG data.  If the TSIG
 * response count is greater than 1 only the timers are hashed.
 * Signed time is set to the current time.  The TSIG record can be
 * added to a packet using tsig_append_rr().
 *
 * The calculated MAC is also stored as the prior MAC, so it can be
 * used as a running MAC.
 */
void tsig_sign(tsig_record_type *tsig);

/*
 * Verify the calculated MAC against the MAC in the TSIG RR.
 *
 * The calculated MAC is also stored as the prior MAC, so it can be
 * used as a running MAC.
 */
int tsig_verify(tsig_record_type *tsig);

/*
 * Find the TSIG RR in QUERY and parse it if present.  Store the
 * parsed results in TSIG.
 *
 * Returns non-zero if no parsing error occurred, use the tsig->status
 * field to find out if the TSIG record was present.
 */
int tsig_find_rr(tsig_record_type *tsig, buffer_type *packet);

/*
 * Call this to analyze the TSIG RR starting at the current location
 * of PACKET. On success true is returned and the results are stored
 * in TSIG.
 *
 * Returns non-zero if no parsing error occurred, use the tsig->status
 * field to find out if the TSIG record was present.
 */
int tsig_parse_rr(tsig_record_type *tsig, buffer_type *packet);

/*
 * Append the TSIG record to the response PACKET.
 */
void tsig_append_rr(tsig_record_type *tsig, buffer_type *packet);

/*
 * The amount of space to reserve in the response for the TSIG data
 * (if required).
 */
size_t tsig_reserved_space(tsig_record_type *tsig);

/*
 * status or error_code must already be in error.
 * prepares content for error packet.
 */
void tsig_error_reply(tsig_record_type *tsig);

/*
 * compare tsig algorithm names case insensitive.
 */
int tsig_strlowercmp(const char* str1, const char* str2);

/*
 * cleanup tsig openssl stuff.
 */
void tsig_finalize(void);

#endif /* _TSIG_H_ */
