/* packet-mikey.c
 * Routines for Multimedia Internet KEYing dissection
 * Copyright 2007, Mikael Magnusson <mikma@users.sourceforge.net>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Ref:
 * https://tools.ietf.org/html/rfc3830  MIKEY
 * https://tools.ietf.org/html/rfc6043  MIKEY-TICKET (ID role required for SAKKE)
 * https://tools.ietf.org/html/rfc6509  MIKEY-SAKKE
 */

/*
 * TODO
 * tvbuff offset in 32-bit variable.
 * Support CHASH
 * Decode Mikey-PK and Mikey-RSA-R with NULL encryption
 */


#include "config.h"

#include <epan/packet.h>
#include <epan/asn1.h>
#include <epan/proto_data.h>
#include "packet-x509af.h"

void proto_register_mikey(void);
void proto_reg_handoff_mikey(void);

#define PORT_MIKEY 2269

static const value_string on_off_vals[] = {
	{ 0, "Off" },
	{ 1, "On" },
	{ 0, NULL }
};

enum data_type_t {
	MIKEY_TYPE_PSK_INIT = 0,
	MIKEY_TYPE_PSK_RESP,
	MIKEY_TYPE_PK_INIT,
	MIKEY_TYPE_PK_RESP,
	MIKEY_TYPE_DH_INIT,
	MIKEY_TYPE_DH_RESP,
	MIKEY_TYPE_ERROR,
	MIKEY_TYPE_DHHMAC_INIT,
	MIKEY_TYPE_DHHMAC_RESP,
	MIKEY_TYPE_RSA_R_INIT,
	MIKEY_TYPE_RSA_R_RESP,
	MIKEY_TYPE_SAKKE_INIT = 26,
	MIKEY_TYPE_SAKKE_RESP
};

static const value_string data_type_vals[] = {
	{ MIKEY_TYPE_PSK_INIT,	  "Pre-shared" },
	{ MIKEY_TYPE_PSK_RESP,	  "PSK ver msg" },
	{ MIKEY_TYPE_PK_INIT,	  "Public key" },
	{ MIKEY_TYPE_PK_RESP,	  "PK ver msg" },
	{ MIKEY_TYPE_DH_INIT,	  "D-H init" },
	{ MIKEY_TYPE_DH_RESP,	  "D-H resp" },
	{ MIKEY_TYPE_ERROR,	  "Error" },
	{ MIKEY_TYPE_DHHMAC_INIT, "DHHMAC init" },
	{ MIKEY_TYPE_DHHMAC_RESP, "DHHMAC resp" },
	{ MIKEY_TYPE_RSA_R_INIT,  "RSA-R I_MSG" },
	{ MIKEY_TYPE_RSA_R_RESP,  "RSA-R R_MSG" },
	{ MIKEY_TYPE_SAKKE_INIT,  "SAKKE" },
	{ MIKEY_TYPE_SAKKE_RESP,  "CS Id map Update" },
	{ 0, NULL }
};
static value_string_ext data_type_vals_ext = VALUE_STRING_EXT_INIT(data_type_vals);

enum cs_id_map_t {
	CS_ID_SRTP = 0
};

static const value_string cs_id_map_vals[] = {
	{ CS_ID_SRTP, "SRTP-ID" },
	{ 0, NULL }
};

enum payload_t {
	PL_HDR      = -1,
	PL_LAST,
	PL_KEMAC,
	PL_PKE,
	PL_DH,
	PL_SIGN,
	PL_T,
	PL_ID,
	PL_CERT,
	PL_CHASH,
	PL_V,
	PL_SP,
	PL_RAND,
	PL_ERR,
	PL_TR       = 13, /* MIKEY-TICKET (6043) */
	PL_IDR,
	PL_RANDR,
	PL_TP,
	PL_TICKET,
	PL_KEY_DATA = 20,
	PL_GENERAL_EXT,
	PL_SAKKE    = 26,
	PL_MAX
};

#define PL_HDR_TEXT	    "Common Header (HDR)"
#define PL_LAST_TEXT	    "Last payload"
#define PL_KEMAC_TEXT	    "Key Data Transport (KEMAC)"
#define PL_PKE_TEXT	    "Envelope Data (PKE)"
#define PL_DH_TEXT	    "DH Data (DH)"
#define PL_SIGN_TEXT	    "Signature (SIGN)"
#define PL_T_TEXT	    "Timestamp (T)"
#define PL_ID_TEXT	    "ID"
#define PL_CERT_TEXT	    "Certificate (CERT)"
#define PL_CHASH_TEXT	    "CHASH"
#define PL_V_TEXT	    "Ver msg (V)"
#define PL_SP_TEXT	    "Security Policy (SP)"
#define PL_RAND_TEXT	    "RAND"
#define PL_ERR_TEXT	    "Error (ERR)"
#define PL_KEY_DATA_TEXT    "Key data (KEY)"
#define PL_IDR_TEXT	    "IDR"
#define PL_GENERAL_EXT_TEXT "General Extension (EXT)"
#define PL_SAKKE_TEXT	    "SAKKE Encapsulated Data (SAKKE)"

static const value_string payload_vals[] = {
	{ PL_HDR,	  PL_HDR_TEXT },
	{ PL_LAST,	  PL_LAST_TEXT },
	{ PL_KEMAC,	  PL_KEMAC_TEXT },
	{ PL_PKE,	  PL_PKE_TEXT },
	{ PL_DH,	  PL_DH_TEXT },
	{ PL_SIGN,	  PL_SIGN_TEXT },
	{ PL_T,		  PL_T_TEXT },
	{ PL_ID,	  PL_ID_TEXT },
	{ PL_CERT,	  PL_CERT_TEXT },
	{ PL_CHASH,	  PL_CHASH_TEXT },
	{ PL_V,		  PL_V_TEXT },
	{ PL_SP,	  PL_SP_TEXT },
	{ PL_RAND,	  PL_RAND_TEXT },
	{ PL_ERR,	  PL_ERR_TEXT },
	{ PL_IDR,	  PL_IDR_TEXT },
	{ PL_KEY_DATA,	  PL_KEY_DATA_TEXT },
	{ PL_GENERAL_EXT, PL_GENERAL_EXT_TEXT },
	{ PL_SAKKE,	  PL_SAKKE_TEXT },
	{ 0, NULL }
};
#if 0 /* First entry (PL_HDR) is -1 and there are gaps so this doesn't work */
static value_string_ext payload_vals_ext = VALUE_STRING_EXT_INIT(payload_vals);
#endif

enum ts_type_t {
	T_NTP_UTC = 0,
	T_NTP,
	T_COUNTER
};

static const value_string ts_type_vals[] = {
	{ T_NTP_UTC, "NTP-UTC" },
	{ T_NTP,     "NTP" },
	{ T_COUNTER, "COUNTER" },
	{ 0, NULL }
};

enum encr_alg_t {
	ENCR_NULL = 0,
	ENCR_AES_CM_128,
	ENCR_AES_KW_128
};

static const value_string encr_alg_vals[] = {
	{ ENCR_NULL,       "NULL" },
	{ ENCR_AES_CM_128, "AES-CM-128" },
	{ ENCR_AES_KW_128, "AES-KW-128" },
	{ 0, NULL }
};

enum oakley_t {
	DH_OAKLEY_5 = 0,
	DH_OAKLEY_1,
	DH_OAKLEY_2
};

static const value_string oakley_vals[] = {
	{ DH_OAKLEY_5, "OAKLEY 5" },
	{ DH_OAKLEY_1, "OAKLEY 1" },
	{ DH_OAKLEY_2, "OAKLEY 2" },
	{ 0, NULL }
};

enum mac_alg_t {
	MAC_NULL = 0,
	MAC_HMAC_SHA_1_160
};

static const value_string mac_alg_vals[] = {
	{ MAC_NULL,           "NULL" },
	{ MAC_HMAC_SHA_1_160, "HMAC-SHA-1-160" },
	{ 0, NULL }
};

enum pke_c_t {
	PKE_C_NO_CACHE = 0,
	PKE_C_CACHE,
	PKE_C_CACHE_CSB
};

static const value_string pke_c_vals[] = {
	{ PKE_C_NO_CACHE,  "No cache" },
	{ PKE_C_CACHE,	   "Cache" },
	{ PKE_C_CACHE_CSB, "Cache for CSB" },
	{ 0, NULL }
};

enum sign_s_t {
	SIGN_S_PKCS1 = 0,
	SIGN_S_PSS,
	SIGN_S_ECCSI
};

static const value_string sign_s_vals[] = {
	{ SIGN_S_PKCS1, "RSA/PKCS#1/1.5" },
	{ SIGN_S_PSS,	"RSA/PSS" },
	{ SIGN_S_ECCSI, "ECCSI" },
	{ 0, NULL }
};

enum id_type_t {
	ID_TYPE_NAI = 0,
	ID_TYPE_URI,
	ID_TYPE_BYTE_STRING
};

static const value_string id_type_vals[] = {
	{ ID_TYPE_NAI,	       "NAI" },
	{ ID_TYPE_URI,	       "URI" },
	{ ID_TYPE_BYTE_STRING, "Byte string" },
	{ 0, NULL }
};

enum id_role_t {
	ID_ROLE_RESERVED = 0,
	ID_ROLE_INIT,
	ID_ROLE_RESP,
	ID_ROLE_KMS,
	ID_ROLE_PSK,
	ID_ROLE_APP,
	ID_ROLE_INIT_KMS,
	ID_ROLE_RESP_KMS
};

static const value_string id_role_vals[] = {
	{ ID_ROLE_RESERVED, "Reserved" },
	{ ID_ROLE_INIT,	    "Initiator (IDRi)" },
	{ ID_ROLE_RESP,	    "Responder (IDRr)" },
	{ ID_ROLE_KMS,	    "KMS (IDRkms)" },
	{ ID_ROLE_PSK,	    "Pre-Shared Key (IDRpsk)" },
	{ ID_ROLE_APP,	    "Application (IDRapp)" },
	{ ID_ROLE_INIT_KMS, "Initiator's KMS (IDRkmsi)" },
	{ ID_ROLE_RESP_KMS, "Responder's KMS (IDRkmsr)" },
	{ 0, NULL }
};

enum cert_type_t {
	CERT_TYPE_X509V3 = 0,
	CERT_TYPE_X509V3_URL,
	CERT_TYPE_X509V3_SIGN,
	CERT_TYPE_X509V3_ENCR
};

static const value_string cert_type_vals[] = {
	{ CERT_TYPE_X509V3,	 "X.509v3" },
	{ CERT_TYPE_X509V3_URL,	 "X.509v3 URL" },
	{ CERT_TYPE_X509V3_SIGN, "X.509v3 Sign" },
	{ CERT_TYPE_X509V3_ENCR, "X.509v3 Encr" },
	{ 0, NULL }
};

enum srtp_policy_type_t {
	SP_ENCR_ALG,
	SP_ENCR_LEN,
	SP_AUTH_ALG,
	SP_AUTH_KEY_LEN,
	SP_SALT_LEN,
	SP_PRF,
	SP_KD_RATE,
	SP_SRTP_ENCR,
	SP_SRTCP_ENCR,
	SP_FEC,
	SP_SRTP_AUTH,
	SP_AUTH_TAG_LEN,
	SP_SRTP_PREFIX,
	SP_MAX
};

#define SP_TEXT_ENCR_ALG     "Encryption algorithm"
#define SP_TEXT_ENCR_LEN     "Session Encr. key length"
#define SP_TEXT_AUTH_ALG     "Authentication algorithm"
#define SP_TEXT_AUTH_KEY_LEN "Session Auth. key length"
#define SP_TEXT_SALT_LEN     "Session Salt key length"
#define SP_TEXT_PRF	     "SRTP Pseudo Random Function"
#define SP_TEXT_KD_RATE	     "Key derivation rate"
#define SP_TEXT_SRTP_ENCR    "SRTP encryption"
#define SP_TEXT_SRTCP_ENCR   "SRTCP encryption"
#define SP_TEXT_FEC	     "Sender's FEC order"
#define SP_TEXT_SRTP_AUTH    "SRTP authentication"
#define SP_TEXT_AUTH_TAG_LEN "Authentication tag length"
#define SP_TEXT_SRTP_PREFIX  "SRTP prefix length"

#if 0
static const value_string srtp_policy_type_vals[] = {
	{ SP_ENCR_ALG,	   SP_TEXT_ENCR_ALG },
	{ SP_ENCR_LEN,	   SP_TEXT_ENCR_LEN },
	{ SP_AUTH_ALG,	   SP_TEXT_AUTH_ALG },
	{ SP_AUTH_KEY_LEN, SP_TEXT_AUTH_KEY_LEN },
	{ SP_SALT_LEN,	   SP_TEXT_SALT_LEN },
	{ SP_PRF,	   SP_TEXT_PRF },
	{ SP_KD_RATE,	   SP_TEXT_KD_RATE },
	{ SP_SRTP_ENCR,	   SP_TEXT_SRTP_ENCR },
	{ SP_SRTCP_ENCR,   SP_TEXT_SRTCP_ENCR },
	{ SP_FEC,	   SP_TEXT_FEC },
	{ SP_SRTP_AUTH,	   SP_TEXT_SRTP_AUTH },
	{ SP_AUTH_TAG_LEN, SP_TEXT_AUTH_TAG_LEN },
	{ SP_SRTP_PREFIX,  SP_TEXT_SRTP_PREFIX },
	{ 0, NULL }
};
#endif

enum sp_encr_alg_t {
	SP_ENCR_NULL = 0,
	SP_ENCR_AES_CM,
	SP_ENCR_AES_F8
};

static const value_string sp_encr_alg_vals[] = {
	{ SP_ENCR_NULL,   "NULL" },
	{ SP_ENCR_AES_CM, "AES-CM" },
	{ SP_ENCR_AES_F8, "AES-F8" },
	{ 0, NULL }
};

enum sp_auth_alg_t {
	SP_AUTH_NULL = 0,
	SP_AUTH_HMAC_SHA_1
};

static const value_string sp_auth_alg_vals[] = {
	{ SP_AUTH_NULL,	      "NULL" },
	{ SP_AUTH_HMAC_SHA_1, "HMAC-SHA-1" },
	{ 0, NULL }
};

enum sp_prf_t {
	SP_PRF_AES_CM = 0
};

static const value_string sp_prf_vals[] = {
	{ SP_PRF_AES_CM, "AES-CM" },
	{ 0, NULL }
};

enum sp_fec_t {
	SP_FEC_SRTP = 0
};

static const value_string sp_fec_vals[] = {
	{ SP_FEC_SRTP, "FEC-SRTP" },
	{ 0, NULL }
};

enum sp_prot_t {
	SP_PROT_TYPE_SRTP = 0
};

static const value_string sp_prot_type_vals[] = {
	{ SP_PROT_TYPE_SRTP, "SRTP" },
	{ 0, NULL }
};

enum prf_func_t {
	PRF_FUNC_MIKEY_1 = 0
};

static const value_string prf_func_vals[] = {
	{ PRF_FUNC_MIKEY_1, "MIKEY-1" },
	{ 0, NULL }
};

enum kv_t {
	KV_NULL = 0,
	KV_SPI,
	KV_INTERVAL
};

static const value_string kv_vals[] = {
	{ KV_NULL,     "Null" },
	{ KV_SPI,      "SPI/MKI" },
	{ KV_INTERVAL, "Interval" },
	{ 0, NULL }
};

enum kd_t {
	KD_TGK = 0,
	KD_TGK_SALT,
	KD_TEK,
	KD_TEK_SALT
};

static const value_string kd_vals[] = {
	{ KD_TGK,      "TGK" },
	{ KD_TGK_SALT, "TGK+SALT" },
	{ KD_TEK,      "TEK" },
	{ KD_TEK_SALT, "TEK+SALT" },
	{ 0, NULL }
};

enum err_t {
	ERR_AUTH_FAILURE = 0,
	ERR_INVALID_TS,
	ERR_INVALID_PRF,
	ERR_INVALID_MAC,
	ERR_INVALID_EA,
	ERR_INVALID_HA,
	ERR_INVALID_DH,
	ERR_INVALID_ID,
	ERR_INVALID_CERT,
	ERR_INVALID_SP,
	ERR_INVALID_SPPAR,
	ERR_INVALID_DT,
	ERR_UNKNOWN
};

static const value_string err_vals[] = {
	{ ERR_AUTH_FAILURE,  "Authentication failure" },
	{ ERR_INVALID_TS,    "Invalid timestamp" },
	{ ERR_INVALID_PRF,   "PRF function not supported" },
	{ ERR_INVALID_MAC,   "MAC algorithm not supported" },
	{ ERR_INVALID_EA,    "Encryption algorithm not supported" },
	{ ERR_INVALID_HA,    "Hash function not supported" },
	{ ERR_INVALID_DH,    "DH group not supported" },
	{ ERR_INVALID_ID,    "ID not supported" },
	{ ERR_INVALID_CERT,  "Certificate not supported" },
	{ ERR_INVALID_SP,    "SP type not supported" },
	{ ERR_INVALID_SPPAR, "SP parameters not supported" },
	{ ERR_INVALID_DT,    "Data type not supported" },
	{ ERR_UNKNOWN,	     "Unspecified error" },
	{ 0, NULL }
};
static value_string_ext err_vals_ext = VALUE_STRING_EXT_INIT(err_vals);

enum genext_t {
	GEN_EXT_VENDOR_ID = 0,
	GEN_EXT_SDP_ID
};

static const value_string genext_type_vals[] = {
	{ GEN_EXT_VENDOR_ID, "Vendor-ID" },
	{ GEN_EXT_SDP_ID,    "SDP-IDs" },
	{ 0, NULL }
};

enum {
	/* HDR */
	POS_HDR_VERSION=0,
	POS_HDR_DATA_TYPE,
	POS_HDR_V,
	POS_HDR_PRF_FUNC,
	POS_HDR_CSB_ID,
	POS_HDR_CS_COUNT,
	POS_HDR_CS_ID_MAP_TYPE,
	POS_ID_SRTP,
	POS_ID_SRTP_NO,
	POS_ID_SRTP_SSRC,
	POS_ID_SRTP_ROC,

	/* KEMAC */
	POS_KEMAC_ENCR_ALG,
	POS_KEMAC_ENCR_DATA_LEN,
	POS_KEMAC_ENCR_DATA,
	POS_KEMAC_MAC_ALG,
	POS_KEMAC_MAC,

	/* PKE */
	POS_PKE_C,
	POS_PKE_DATA_LEN,
	POS_PKE_DATA,

	/* DH */
	POS_DH_GROUP,
	POS_DH_VALUE,
	POS_DH_RESERV,
	POS_DH_KV,

	/* SIGN */
	POS_SIGNATURE_LEN,
	POS_SIGNATURE,
	POS_SIGN_S_TYPE,

	/* T */
	POS_TS_TYPE,
	POS_TS_NTP,

	/* ID/IDR */
	POS_ID_ROLE,
	POS_ID_TYPE,
	POS_ID_LEN,
	POS_ID,

	/* CERT */
	POS_CERT_TYPE,
	POS_CERT_LEN,
	POS_CERTIFICATE,

	/* V */
	POS_V_AUTH_ALG,
	POS_V_DATA,

	/* SP */
	POS_SP_NO,
	POS_SP_TYPE,
	POS_SP_PARAM_LEN,
/* 	POS_SP_PARAM, */

	/* SP param */
	POS_SP_PARAM_F,
	POS_SP_PARAM_F_TYPE,
	POS_SP_PARAM_F_LEN,
	POS_SP_PARAM_F_VALUE,

	/* RAND */
	POS_RAND_LEN,
	POS_RAND,

	/* Error */
	POS_ERR_NO,
	POS_ERR_RESERVED,

	/* Key data */
	POS_KEY_DATA_TYPE,
	POS_KEY_DATA_KV,
	POS_KEY_DATA_LEN,
	POS_KEY_DATA,
	POS_KEY_SALT_LEN,
	POS_KEY_SALT,
	POS_KEY_KV_FROM_LEN,
	POS_KEY_KV_FROM,
	POS_KEY_KV_TO_LEN,
	POS_KEY_KV_TO,
	POS_KEY_KV_SPI_LEN,
	POS_KEY_KV_SPI,

	/* General Ext. */
	POS_GENERAL_EXT_TYPE,
	POS_GENERAL_EXT_LEN,
	POS_GENERAL_EXT_DATA,
	POS_GENERAL_EXT_VALUE,

	/* SAKKE */
	POS_SAKKE_PARAMS,
	POS_SAKKE_ID_SCHEME,
	POS_SAKKE_LEN,
	POS_SAKKE_DATA,

	/* MIKEY */
	POS_PAYLOAD_STR,
	POS_NEXT_PAYLOAD,

	/* Unused */
/* 	POS_PAYLOAD, */

	MAX_POS
};

typedef struct tag_mikey_t {
	guint8 type;
} mikey_t;

typedef int (*mikey_dissector_t)(mikey_t *, tvbuff_t *, packet_info *, proto_tree *);
struct mikey_dissector_entry {
	int type;
	mikey_dissector_t dissector;
};

/* Forward declaration we need below */
static int dissect_payload(int payload, mikey_t *mikey, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);


/* Initialize the protocol and registered fields */
static int proto_mikey = -1;
static int hf_mikey[MAX_POS+1];
static int hf_mikey_sp_param[SP_MAX+1];
static int hf_mikey_pl[PL_MAX];

/* Initialize the subtree pointers */
static gint ett_mikey = -1;
static gint ett_mikey_payload = -1;
static gint ett_mikey_sp_param = -1;
static gint ett_mikey_hdr_id = -1;
static gint ett_mikey_enc_data = -1;

static dissector_handle_t mikey_handle;

static const struct mikey_dissector_entry *
mikey_dissector_lookup(const struct mikey_dissector_entry *map, int type)
{
	unsigned int i;
	for (i = 0; map[i].dissector != NULL; i++) {
		if (map[i].type == type) {
			return &map[i];
		}
	}

	return NULL;
}

static void
add_next_payload(tvbuff_t *tvb, proto_tree *tree, int offset)
{
	proto_tree_add_item(tree, hf_mikey[POS_NEXT_PAYLOAD], tvb, offset, 1, ENC_BIG_ENDIAN);
}


static int
dissect_payload_cs_id_srtp(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_,  proto_tree *tree)
{
	if (tree) {
		proto_item *id_ti;
		proto_tree *id_tree;
		guint8	    no;
		guint32	    ssrc;
		guint32	    roc;

		no   = tvb_get_guint8(tvb, 0);
		ssrc = tvb_get_ntohl(tvb, 1);
		roc  = tvb_get_ntohl(tvb, 5);

		id_ti = proto_tree_add_none_format(tree, hf_mikey[POS_ID_SRTP], tvb, 0, 9,
						   "SRTP ID: Policy: %d, SSRC: 0x%x, ROC: 0x%x", no, ssrc, roc);
		id_tree = proto_item_add_subtree(id_ti, ett_mikey_hdr_id);

		proto_tree_add_item(id_tree, hf_mikey[POS_ID_SRTP_NO],	 tvb, 0, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(id_tree, hf_mikey[POS_ID_SRTP_SSRC], tvb, 1, 4, ENC_BIG_ENDIAN);
		proto_tree_add_item(id_tree, hf_mikey[POS_ID_SRTP_ROC],	 tvb, 5, 4, ENC_BIG_ENDIAN);
	}
	return 9;
}

static const struct mikey_dissector_entry cs_id_map[] = {
	{ CS_ID_SRTP, dissect_payload_cs_id_srtp },
	{ 0, NULL }
};

static int
dissect_payload_cs_id(int type, mikey_t *mikey, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	const struct mikey_dissector_entry *entry;

	entry = mikey_dissector_lookup(cs_id_map, type);

	if (!entry || !entry->dissector) {
		return 0;
	}

	return entry->dissector(mikey, tvb, pinfo, tree);

}

static int
dissect_payload_hdr(mikey_t *mikey, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	int    offset = 0;
	guint8 cs_id_map_type;
	guint8 ncs;
	int    i;

	tvb_ensure_bytes_exist(tvb, offset, 10);
	mikey->type = tvb_get_guint8(tvb, offset+1);
	ncs = tvb_get_guint8(tvb, offset+8);
	cs_id_map_type = tvb_get_guint8(tvb, offset+9);

	if (tree) {
		proto_item* parent;
		proto_tree_add_item(tree, hf_mikey[POS_HDR_VERSION],
					tvb, offset+0, 1, ENC_BIG_ENDIAN);

		proto_tree_add_item(tree, hf_mikey[POS_HDR_DATA_TYPE], tvb, offset+1, 1, ENC_BIG_ENDIAN);
		parent = proto_tree_get_parent(tree);
		proto_item_append_text(parent, " Type: %s",
				       val_to_str_ext_const(mikey->type, &data_type_vals_ext, "Unknown"));

		add_next_payload(tvb, tree, offset+2);

		proto_tree_add_item(tree, hf_mikey[POS_HDR_V], tvb, offset+3, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_HDR_PRF_FUNC], tvb, offset+3, 1, ENC_BIG_ENDIAN);

		proto_tree_add_item(tree, hf_mikey[POS_HDR_CSB_ID], tvb, offset+4, 4, ENC_BIG_ENDIAN);

		proto_tree_add_item(tree, hf_mikey[POS_HDR_CS_COUNT], tvb, offset+8, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_HDR_CS_ID_MAP_TYPE], tvb, offset+9, 1, ENC_BIG_ENDIAN);
	}

	offset += 10;
	for (i=0; i < ncs; i++) {
		tvbuff_t *sub_tvb;
		int	  len;

		sub_tvb = tvb_new_subset_remaining(tvb, offset);
		len = dissect_payload_cs_id(cs_id_map_type, mikey, sub_tvb, pinfo, tree);

		if (len < 0)
			return 0;

		offset += len;
	}

	return offset;
}

static int
dissect_payload_kemac(mikey_t *mikey, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	int	offset = 0;
	guint8	encr_alg;
	guint16	encr_length;
	guint16	mac_length;
	guint8	mac_alg;

	encr_alg    = tvb_get_guint8(tvb, offset+1);
	encr_length = tvb_get_ntohs(tvb, offset+2);
	tvb_ensure_bytes_exist(tvb, offset+4, encr_length+1);
	mac_alg     = tvb_get_guint8(tvb, offset+4+encr_length);

	if (tree) {
		tvbuff_t *sub_tvb;
		proto_tree_add_item(tree, hf_mikey[POS_KEMAC_ENCR_ALG],      tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_KEMAC_ENCR_DATA_LEN], tvb, 2, 2, ENC_BIG_ENDIAN);
		/* TODO: Add key decode for MIKEY_TYPE_PK_INIT and MIKEY_TYPE_RSA_R_RESP with NULL encryption */
		if ((encr_alg == ENCR_NULL) && (mikey->type == MIKEY_TYPE_PSK_INIT) && (encr_length > 0)) {
			proto_item *key_data_item;
			proto_tree *key_data_tree;
			/* We can decode easily the Key Data if NULL encryption is used */
			key_data_item = proto_tree_add_item(tree, hf_mikey_pl[PL_KEY_DATA], tvb, 4, encr_length, ENC_NA);
			key_data_tree = proto_item_add_subtree(key_data_item, ett_mikey_enc_data);

			sub_tvb = tvb_new_subset_length(tvb, offset+4, encr_length);
			dissect_payload(PL_KEY_DATA, mikey, sub_tvb, pinfo, key_data_tree);
		} else {
			/* If Key Data is encrypted, show only the encr_data */
			proto_tree_add_item(tree, hf_mikey[POS_KEMAC_ENCR_DATA], tvb, 4, encr_length, ENC_NA);
		}
		proto_tree_add_item(tree, hf_mikey[POS_KEMAC_MAC_ALG], tvb, 4+encr_length, 1, ENC_BIG_ENDIAN);
	}

	switch (mac_alg) {
	case MAC_NULL:
		mac_length = 0;
		break;
	case MAC_HMAC_SHA_1_160:
		mac_length = 160/8;
		break;
	default:
		return 0;
	}

	proto_tree_add_item(tree, hf_mikey[POS_KEMAC_MAC], tvb, 4+encr_length+1, mac_length, ENC_NA);

	return 4+encr_length+1+mac_length;
}

static int
dissect_payload_pke(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint16 length;

	length = tvb_get_ntohs(tvb, offset+1) &0x3ff;

	if (tree) {
		proto_tree_add_item(tree, hf_mikey[POS_PKE_C], tvb, 1, 2, ENC_BIG_ENDIAN);

		proto_tree_add_item(tree, hf_mikey[POS_PKE_DATA_LEN], tvb, 1, 2, ENC_BIG_ENDIAN);
	}

	proto_tree_add_item(tree, hf_mikey[POS_PKE_DATA], tvb, 3, length, ENC_NA);
	return 3 + length;
}

static int
dissect_payload_dh(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int    offset = 0;
	guint8 dh_group;
	int    dh_length;
	guint8 kv;

	dh_group = tvb_get_guint8(tvb, offset+1);

	switch (dh_group) {
	case DH_OAKLEY_5:
		dh_length = 1536/8;
		break;
	case DH_OAKLEY_1:
		dh_length = 768/8;
		break;
	case DH_OAKLEY_2:
		dh_length = 1024/8;
		break;
	default:
		return 0;
	}

	kv = tvb_get_guint8(tvb, offset+2+dh_length) & 0x0f;

	if (tree) {
		proto_tree_add_item(tree, hf_mikey[POS_DH_GROUP], tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_DH_VALUE], tvb, 2, dh_length, ENC_NA);
		proto_tree_add_item(tree, hf_mikey[POS_DH_RESERV], tvb, 2+dh_length, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_DH_KV], tvb, 2+dh_length, 1, ENC_BIG_ENDIAN);
	}

	if (kv != 0) {
		return 0;
	}

	return 2 + dh_length + 1;
}

static int
dissect_payload_sign(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint16 length;

	length = ((tvb_get_guint8(tvb, offset+0) & 0x0f) << 8) + tvb_get_guint8(tvb, offset+1);

	if (tree) {
		proto_tree_add_item(tree, hf_mikey[POS_SIGN_S_TYPE], tvb, 0, 2, ENC_BIG_ENDIAN);
		proto_tree_add_uint(tree, hf_mikey[POS_SIGNATURE_LEN], tvb, 0, 2, length);
	}

	proto_tree_add_item(tree, hf_mikey[POS_SIGNATURE], tvb, 2, length, ENC_NA);
	return 2 + length;
}

static int
dissect_payload_t(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	guint8 ts_type;
	int    offset = 0;
	int    len    = 0;

	ts_type = tvb_get_guint8(tvb, offset+1);

	if (tree) {
		proto_tree *parent;
		parent = proto_tree_get_parent(tree);
		proto_item_append_text(parent, " Type: %s", val_to_str_const(ts_type, ts_type_vals, "Unknown"));
		proto_tree_add_item(tree, hf_mikey[POS_TS_TYPE], tvb, offset+1, 1, ENC_BIG_ENDIAN);
	}

	switch (ts_type) {
	case T_NTP:
	case T_NTP_UTC:
		proto_tree_add_item(tree, hf_mikey[POS_TS_NTP], tvb, offset+2, 8, ENC_TIME_NTP|ENC_BIG_ENDIAN);
		len = 10;
		break;
	case T_COUNTER:
		len = 6;
		break;
	default:
		len = 0;
		break;
	}

	return len;
}

static int
dissect_payload_id(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint8	type;
	guint16	length;

	type   = tvb_get_guint8(tvb, offset+1);
	length = tvb_get_ntohs(tvb,  offset+2);
	if (tree) {
		proto_tree_add_item(tree, hf_mikey[POS_ID_TYPE], tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_ID_LEN], tvb, 2, 2, ENC_BIG_ENDIAN);
	}

	if (tree) {
		proto_item* parent;
		const guint8* pos_id;
		proto_tree_add_item_ret_string(tree, hf_mikey[POS_ID], tvb, 4, length, ENC_ASCII|ENC_NA, wmem_packet_scope(), &pos_id);

		parent = proto_tree_get_parent(tree);
		proto_item_append_text(parent, " %s: %s", val_to_str_const(type, id_type_vals, "Unknown"), pos_id);
	}

	return 4 + length;
}

static int
dissect_payload_idr(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint8	type;
	guint16	length;

	type = tvb_get_guint8(tvb, offset+2);
	length = tvb_get_ntohs(tvb, offset+3);
	if (tree) {
		proto_tree_add_item(tree, hf_mikey[POS_ID_ROLE], tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_ID_TYPE], tvb, 2, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_ID_LEN],  tvb, 3, 2, ENC_BIG_ENDIAN);
	}

	if (tree) {
		proto_item *parent;
		const guint8* pos_id;
		proto_tree_add_item_ret_string(tree, hf_mikey[POS_ID], tvb, 5, length, ENC_ASCII|ENC_NA, wmem_packet_scope(), &pos_id);

		parent = proto_tree_get_parent(tree);
		proto_item_append_text(parent, " %s: %s", val_to_str_const(type, id_type_vals, "Unknown"), pos_id);
	}

	return 5 + length;
}

static int
dissect_payload_cert(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	int	     offset = 0;
	guint8	     type;
	guint16	     length;
	tvbuff_t    *subtvb;
	asn1_ctx_t   asn1_ctx;

	asn1_ctx_init(&asn1_ctx, ASN1_ENC_BER, TRUE, pinfo);

	type   = tvb_get_guint8(tvb, offset+1);
	length = tvb_get_ntohs(tvb, offset+2);

	tvb_ensure_bytes_exist(tvb, offset+4, length);

	if (tree) {
		proto_item *parent;
		parent = proto_tree_get_parent(tree);
		proto_tree_add_item(tree, hf_mikey[POS_CERT_TYPE], tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_CERT_LEN], tvb, 1, 2, ENC_BIG_ENDIAN);

		proto_item_append_text(parent, " Type: %s", val_to_str_const(type, cert_type_vals, "Unknown"));
	}

	subtvb = tvb_new_subset_length(tvb, offset+4, length);
	dissect_x509af_Certificate(FALSE, subtvb, 0, &asn1_ctx, tree, hf_mikey[POS_CERTIFICATE]);

	return 4 + length;
}

static int
dissect_payload_v(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint16 length;
	guint8	alg;

	alg = tvb_get_guint8(tvb, offset+1);

	proto_tree_add_item(tree, hf_mikey[POS_V_AUTH_ALG], tvb, 1, 1, ENC_BIG_ENDIAN);

	switch (alg) {
	case MAC_NULL:
		length = 0;
		break;
	case MAC_HMAC_SHA_1_160:
		length = 160/8;
		break;
	default:
		return 0;
	}

	proto_tree_add_item(tree, hf_mikey[POS_V_DATA], tvb, 2, length, ENC_NA);

	return 2 + length;
}

static int
dissect_payload_sp_param(enum sp_prot_t proto, tvbuff_t *tvb, proto_tree *tree)
{
	int    offset = 0;
	guint8 type;
	guint8 length;
	int    hfindex;

	type = tvb_get_guint8(tvb, offset+0);
	length = tvb_get_guint8(tvb, offset+1);

	/* Default */
	hfindex = hf_mikey[POS_SP_PARAM_F];

	switch(proto) {
	case SP_PROT_TYPE_SRTP:
		if (type < array_length(hf_mikey_sp_param))
			hfindex = hf_mikey_sp_param[type];
		break;
	}

	if (tree) {
		proto_item *param_ti;
		proto_tree *param_tree;
		/*
		 * All the parameters in question are either FT_BYTES,
		 * in which case the byte order is inapplicable, or
		 * FT_UINT8, in which case it could be given as
		 * FT_BIG_ENDIAN as per bigger FT_UINT values, but
		 * ENC_NA also works, as there's only one byte.
		 */
		param_ti = proto_tree_add_item(tree, hfindex, tvb, 2, length, ENC_NA);
		param_tree = proto_item_add_subtree(param_ti, ett_mikey_sp_param);

		proto_tree_add_item(param_tree, hf_mikey[POS_SP_PARAM_F_TYPE], tvb, 0, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(param_tree, hf_mikey[POS_SP_PARAM_F_LEN], tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(param_tree, hf_mikey[POS_SP_PARAM_F_VALUE], tvb, 2, length, ENC_NA);
	}

	return 2+length;
}

static int
dissect_payload_sp(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	       offset = 0;
	guint16	       length;
	int	       sub_pos;
	guint8	       no;
	enum sp_prot_t type;

	length = tvb_get_ntohs(tvb, offset+3);
	no     = tvb_get_guint8(tvb, offset+1);
	type   = (enum sp_prot_t)tvb_get_guint8(tvb, offset+2);

	if (tree) {
		proto_item *parent;
		parent = proto_tree_get_parent(tree);
		proto_tree_add_item(tree, hf_mikey[POS_SP_NO],        tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_SP_TYPE],      tvb, 2, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_SP_PARAM_LEN], tvb, 3, 2, ENC_BIG_ENDIAN);

		proto_item_append_text(parent, " No: %d, Type: %s", no,
				       val_to_str_const(type, sp_prot_type_vals, "Unknown"));
	}

	tvb_ensure_bytes_exist(tvb, offset+5, length);
/* 	proto_tree_add_item(tree, hf_mikey[POS_SP_PARAM], tvb, 5, length, ENC_NA); */

	offset += 5;
	sub_pos = 0;

	while (sub_pos < length) {
		int	  param_len;
		tvbuff_t *subtvb;

		subtvb = tvb_new_subset_length(tvb, offset+sub_pos, length-sub_pos);
		param_len = dissect_payload_sp_param(type, subtvb, tree);

		if (param_len < 0)
			return 0;

		sub_pos += param_len;
	}

	return 5 + length;
}

static int
dissect_payload_rand(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint16 length;

	length = tvb_get_guint8(tvb, offset+1);

	proto_tree_add_item(tree, hf_mikey[POS_RAND_LEN], tvb, 1, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(tree, hf_mikey[POS_RAND], tvb, 2, length, ENC_NA);

	return 2 + length;
}

static int
dissect_payload_err(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	if (tree) {
		proto_item *parent;
		guint8 err_no;
		err_no = tvb_get_guint8(tvb, 1);
		proto_tree_add_item(tree, hf_mikey[POS_ERR_NO], tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_ERR_RESERVED], tvb, 2, 2, ENC_NA);
		parent = proto_tree_get_parent(tree);
		proto_item_append_text(parent, ": %s", val_to_str_ext_const(err_no, &err_vals_ext, "Unknown"));
	}

	return 4;
}

static int
dissect_payload_keydata(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	guint16	offset;
	guint16	data_len;
	guint8	key_type;
	guint8	kv_type;

	offset = 0;
	key_type = tvb_get_guint8(tvb, 1) >> 4;
	kv_type  = tvb_get_guint8(tvb, 1) & 0x0f;
	data_len = tvb_get_ntohs(tvb, 2);

	offset += 4;

	if (tree) {
		proto_item *parent;
		proto_tree_add_item(tree, hf_mikey[POS_KEY_DATA_TYPE], tvb, 1, 1,        ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_KEY_DATA_KV],   tvb, 1, 1,        ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_KEY_DATA_LEN],  tvb, 2, 2,        ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_KEY_DATA],      tvb, 4, data_len, ENC_NA);

		parent = proto_tree_get_parent(tree);
		proto_item_append_text(parent, " Type: %s", val_to_str_const(key_type, kd_vals, "Unknown"));
	}

	offset += data_len;

	/* Dissect SALT key */
	if ((key_type == KD_TGK_SALT) || (key_type == KD_TEK_SALT)) {
		guint16	salt_len;
		salt_len = tvb_get_ntohs(tvb, offset);
		if (salt_len > 0) {
			proto_tree_add_item(tree, hf_mikey[POS_KEY_SALT_LEN], tvb, offset, 2, ENC_BIG_ENDIAN);
			proto_tree_add_item(tree, hf_mikey[POS_KEY_SALT], tvb, offset+2, salt_len, ENC_NA);
		}
		offset += 2+salt_len;
	}

	/* Dissect Key Validity */
	if (kv_type == KV_INTERVAL) {
		guint16	kv_from_len;
		guint16	kv_to_len;

		kv_from_len = tvb_get_guint8(tvb, offset);
		proto_tree_add_item(tree, hf_mikey[POS_KEY_KV_FROM_LEN], tvb, offset, 1, ENC_BIG_ENDIAN);
		if (kv_from_len > 0) {
			proto_tree_add_item(tree, hf_mikey[POS_KEY_KV_FROM], tvb, offset+1, kv_from_len, ENC_NA);
		}
		offset += 1+kv_from_len;

		kv_to_len = tvb_get_guint8(tvb, offset);
		proto_tree_add_item(tree, hf_mikey[POS_KEY_KV_TO_LEN], tvb, offset, 1, ENC_BIG_ENDIAN);
		if (kv_to_len > 0) {
			proto_tree_add_item(tree, hf_mikey[POS_KEY_KV_TO], tvb, offset+1, kv_to_len, ENC_NA);
		}
		offset += 1+kv_to_len;
	} else if (kv_type == KV_SPI) {
		guint16	kv_spi_len;

		kv_spi_len = tvb_get_guint8(tvb, offset);
		proto_tree_add_item(tree, hf_mikey[POS_KEY_KV_SPI_LEN], tvb, offset, 1, ENC_BIG_ENDIAN);
		if (kv_spi_len > 0) {
			proto_tree_add_item(tree, hf_mikey[POS_KEY_KV_SPI], tvb, offset+1, kv_spi_len, ENC_NA);
		}
		offset += 1+kv_spi_len;
	}

	return offset;
}

static int
dissect_payload_general_ext(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint8	type;
	guint16	data_len;

	type	 = tvb_get_guint8(tvb, offset+1);
	data_len = tvb_get_ntohs(tvb, offset+2);

	if (tree) {
		proto_tree_add_item(tree, hf_mikey[POS_GENERAL_EXT_TYPE], tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_GENERAL_EXT_LEN], tvb, 2, 2, ENC_BIG_ENDIAN);
	}

	if (tree) {
		proto_item *parent;

		parent = proto_tree_get_parent(tree);
		if (type == 1) {
			/* For SDP-IDs, show a string instead of raw bytes */
			proto_tree_add_item(tree, hf_mikey[POS_GENERAL_EXT_VALUE], tvb, 4, data_len, ENC_ASCII|ENC_NA);
		} else {
			proto_tree_add_item(tree, hf_mikey[POS_GENERAL_EXT_DATA], tvb, 4, data_len, ENC_NA);
		}
		proto_item_append_text(parent, " Type: %s", val_to_str_const(type, genext_type_vals, "Unknown"));
	}
	return 4 + data_len;
}

static int
dissect_payload_sakke(mikey_t *mikey _U_, tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int	offset = 0;
	guint16 data_len;

	data_len = tvb_get_ntohs(tvb, offset+3);

	if (tree) {
		proto_tree_add_item(tree, hf_mikey[POS_SAKKE_PARAMS],    tvb, 1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_SAKKE_ID_SCHEME], tvb, 2, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(tree, hf_mikey[POS_SAKKE_LEN],       tvb, 3, 2, ENC_BIG_ENDIAN);
	}

	proto_tree_add_item(tree, hf_mikey[POS_SAKKE_DATA], tvb, 5, data_len, ENC_NA);
	return 5 + data_len;
}

static const struct mikey_dissector_entry payload_map[] = {
	{ PL_HDR,	  dissect_payload_hdr },
	{ PL_KEMAC,	  dissect_payload_kemac },
	{ PL_PKE,	  dissect_payload_pke },
	{ PL_DH,	  dissect_payload_dh },
	{ PL_SIGN,	  dissect_payload_sign },
	{ PL_T,		  dissect_payload_t },
	{ PL_ID,	  dissect_payload_id },
	{ PL_CERT,	  dissect_payload_cert },
	{ PL_V,		  dissect_payload_v },
	{ PL_SP,	  dissect_payload_sp },
	{ PL_RAND,	  dissect_payload_rand },
	{ PL_ERR,	  dissect_payload_err },
	{ PL_IDR,	  dissect_payload_idr },
	{ PL_KEY_DATA,	  dissect_payload_keydata },
	{ PL_GENERAL_EXT, dissect_payload_general_ext },
	{ PL_SAKKE,	  dissect_payload_sakke },
	{ 0, NULL }
};

static int
dissect_payload(int payload, mikey_t *mikey, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	const struct mikey_dissector_entry *entry;

	entry = mikey_dissector_lookup(payload_map, payload);

	if (!entry || !entry->dissector) {
		return 0;
	}

	return entry->dissector(mikey, tvb, pinfo, tree);
}

/* MIKEY dissector */
static int
dissect_mikey(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
	proto_item *ti	       = NULL;
	proto_tree *mikey_tree = NULL;
	int	    offset     = 0;
	int	    next_payload_offset;
	int	    payload;
	mikey_t	   *mikey;

	mikey = (mikey_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_mikey, 0);

	if (!mikey) {
		mikey = wmem_new0(wmem_file_scope(), mikey_t);
		mikey->type = -1;
		p_add_proto_data(wmem_file_scope(), pinfo, proto_mikey, 0, mikey);
	}


	tvb_ensure_bytes_exist(tvb, offset, 3);
	next_payload_offset = offset + 2;
	payload = -1;

	if (tree) {
		ti = proto_tree_add_item(tree, proto_mikey, tvb, 0, -1, ENC_NA);
		mikey_tree = proto_item_add_subtree(ti, ett_mikey);
	}

	while (payload != 0) {
		int	    len;
		proto_item *sub_ti	       = NULL;
		proto_tree *mikey_payload_tree = NULL;
		int	    next_payload;
		tvbuff_t   *subtvb;

		next_payload = tvb_get_guint8(tvb, next_payload_offset);
		subtvb = tvb_new_subset_remaining(tvb, offset);

		if (mikey_tree) {
			int hf = payload;

			if (hf >= PL_MAX)
				return -1;

			if (hf == -1)
				hf = 0;

			sub_ti = proto_tree_add_item(mikey_tree, hf_mikey_pl[hf], subtvb, 0, -1, ENC_NA);

			mikey_payload_tree = proto_item_add_subtree(sub_ti, ett_mikey_payload);
			if ((payload != PL_HDR) && (payload != PL_SIGN))
				add_next_payload(tvb, mikey_payload_tree, next_payload_offset);
		}

		len = dissect_payload(payload, mikey, subtvb, pinfo, mikey_payload_tree);
		if (len <= 0) {
			/* protocol violation or invalid data, stop dissecting
			 * but accept the data retrieved so far */
			return tvb_captured_length(tvb);
		}

		if (sub_ti)
			proto_item_set_len(sub_ti, len);

		if (payload == PL_SIGN)
			break;

		payload = next_payload;
		offset += len;
		next_payload_offset = offset;
	}

	if (ti) {
		proto_item_append_text(ti, ": %s",
				       val_to_str_ext_const(mikey->type, &data_type_vals_ext, "Unknown"));
	}

	col_append_str(pinfo->cinfo, COL_PROTOCOL, "/MIKEY");

	col_append_fstr(pinfo->cinfo, COL_INFO, ", Mikey: %s",
				val_to_str_ext_const(mikey->type, &data_type_vals_ext, "Unknown"));

	/* Return the amount of data this dissector was able to dissect */
	return tvb_captured_length(tvb);
}


/* Register the protocol with Wireshark */

void
proto_register_mikey(void)
{

	/* Setup list of header fields */
	static hf_register_info hf[] = {
		/* Payload types */
		{ &hf_mikey_pl[PL_HDR+1],
		  { PL_HDR_TEXT, "mikey.hdr",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_KEMAC],
		  { PL_KEMAC_TEXT, "mikey.kemac",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_PKE],
		  { PL_PKE_TEXT, "mikey.pke",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_DH],
		  { PL_DH_TEXT, "mikey.dh",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_SIGN],
		  { PL_SIGN_TEXT, "mikey.sign",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_T],
		  { PL_T_TEXT, "mikey.t",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_ID],
		  { PL_ID_TEXT, "mikey.id",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_CERT],
		  { PL_CERT_TEXT, "mikey.cert",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_CHASH],
		  { PL_CHASH_TEXT, "mikey.chash",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_V],
		  { PL_V_TEXT, "mikey.v",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_SP],
		  { PL_SP_TEXT, "mikey.sp",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_RAND],
		  { PL_RAND_TEXT, "mikey.rand",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_ERR],
		  { PL_ERR_TEXT, "mikey.err",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_IDR],
		  { PL_IDR_TEXT, "mikey.idr",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_KEY_DATA],
		  { PL_KEY_DATA_TEXT, "mikey.key",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_GENERAL_EXT],
		  { PL_GENERAL_EXT_TEXT, "mikey.ext",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_pl[PL_SAKKE],
		  { PL_SAKKE_TEXT, "mikey.sakke",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* Common Header payload (HDR) */
		{ &hf_mikey[POS_HDR_VERSION],
		  { "Version", "mikey.version",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_HDR_DATA_TYPE],
		  { "Data Type", "mikey.type",
		    FT_UINT8, BASE_DEC|BASE_EXT_STRING, &data_type_vals_ext, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_NEXT_PAYLOAD],
		  { "Next Payload", "mikey.next_payload",
		    FT_UINT8, BASE_DEC, VALS(payload_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_HDR_V],
		  { "V", "mikey.v.set",
		    FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x80,
		    NULL, HFILL }},
		{ &hf_mikey[POS_HDR_PRF_FUNC],
		  { "PRF func", "mikey.prf_func",
		    FT_UINT8, BASE_DEC, VALS(prf_func_vals), 0x7f,
		    NULL, HFILL }},
		{ &hf_mikey[POS_HDR_CSB_ID],
		  { "CSB ID", "mikey.csb_id",
		    FT_UINT32, BASE_HEX, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_HDR_CS_COUNT],
		  { "#CS", "mikey.cs_count",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_HDR_CS_ID_MAP_TYPE],
		  { "CS ID map type", "mikey.cs_id_map_type",
		    FT_UINT8, BASE_DEC, VALS(cs_id_map_vals), 0x0,
		    NULL, HFILL }},

		/* SRTP ID */
		{ &hf_mikey[POS_ID_SRTP],
		  { "SRTP ID", "mikey.srtp_id",
		    FT_NONE, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_ID_SRTP_NO],
		  { "Policy No", "mikey.srtp_id.policy_no",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_ID_SRTP_SSRC],
		  { "SSRC", "mikey.srtp_id.ssrc",
		    FT_UINT32, BASE_HEX, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_ID_SRTP_ROC],
		  { "ROC", "mikey.srtp_id.roc",
		    FT_UINT32, BASE_HEX, NULL, 0x0,
		    NULL, HFILL }},

		/* Key Data Transport payload (KEMAC) */
		{ &hf_mikey[POS_KEMAC_ENCR_ALG],
		  { "Encr alg", "mikey.kemac.encr_alg",
		    FT_UINT8, BASE_DEC, VALS(encr_alg_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEMAC_ENCR_DATA_LEN],
		  { "Key data len", "mikey.kemac.key_data_len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEMAC_ENCR_DATA],
		  { "Key data", "mikey.kemac.key_data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEMAC_MAC_ALG],
		  { "Mac alg", "mikey.kemac.mac_alg",
		    FT_UINT8, BASE_DEC, VALS(mac_alg_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEMAC_MAC],
		  { "MAC", "mikey.kemac.mac",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* Envelope Data payload (PKE) */
		{ &hf_mikey[POS_PKE_C],
		  { "C", "mikey.pke.c",
		    FT_UINT16, BASE_DEC, VALS(pke_c_vals), 0xc000,
		    NULL, HFILL }},
		{ &hf_mikey[POS_PKE_DATA_LEN],
		  { "Data len", "mikey.pke.len",
		    FT_UINT16, BASE_DEC, NULL, 0x3fff,
		    NULL, HFILL }},
		{ &hf_mikey[POS_PKE_DATA],
		  { "Data", "mikey.pke.data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* DH data payload (DH) */
		{ &hf_mikey[POS_DH_GROUP],
		  { "DH-Group", "mikey.dh.group",
		    FT_UINT8, BASE_DEC, VALS(oakley_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_DH_VALUE],
		  { "DH-Value", "mikey.dh.value",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_DH_RESERV],
		  { "Reserv", "mikey.dh.reserv",
		    FT_UINT8, BASE_HEX, NULL, 0xf0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_DH_KV],
		  { "KV", "mikey.dh.kv",
		    FT_UINT8, BASE_DEC, VALS(kv_vals), 0x0f,
		    NULL, HFILL }},

		/* Signature payload (SIGN) */
		{ &hf_mikey[POS_SIGN_S_TYPE],
		  { "Signature type", "mikey.sign.type",
		    FT_UINT16, BASE_DEC, VALS(sign_s_vals), 0xf000,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SIGNATURE_LEN],
		  { "Signature len", "mikey.sign.len",
		    FT_UINT16, BASE_DEC, NULL, 0x0fff,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SIGNATURE],
		  { "Signature", "mikey.sign.data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* Timestamp payload (T) */
		{ &hf_mikey[POS_TS_TYPE],
		  { "TS type", "mikey.t.ts_type",
		    FT_UINT8, BASE_DEC, VALS(ts_type_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_TS_NTP],
		  { "NTP timestamp", "mikey.t.ntp",
		    FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0,
		    NULL, HFILL }},

		{ &hf_mikey[POS_PAYLOAD_STR],
		  { "Payload", "mikey.payload",
		    FT_STRING, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* ID payload (ID) */
		{ &hf_mikey[POS_ID_TYPE],
		  { "ID type", "mikey.id.type",
		    FT_UINT8, BASE_DEC, VALS(id_type_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_ID_LEN],
		  { "ID len", "mikey.id.len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_ID],
		  { "ID", "mikey.id.data",
		    FT_STRING, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* Certificate payload (CERT) */
		{ &hf_mikey[POS_CERT_LEN],
		  { "Certificate len", "mikey.cert.len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_CERT_TYPE],
		  { "Certificate type", "mikey.cert.type",
		    FT_UINT8, BASE_DEC, VALS(cert_type_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_CERTIFICATE],
		  { "Certificate", "mikey.cert.data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* Ver msg payload (V) */
		{ &hf_mikey[POS_V_AUTH_ALG],
		  { "Auth alg", "mikey.v.auth_alg",
		    FT_UINT8, BASE_DEC, VALS(mac_alg_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_V_DATA],
		  { "Ver data", "mikey.v.ver_data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* Security Policy payload (SP) */
		{ &hf_mikey[POS_SP_NO],
		  { "Policy No", "mikey.sp.no",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SP_TYPE],
		  { "Protocol type", "mikey.sp.proto_type",
		    FT_UINT8, BASE_DEC, VALS(sp_prot_type_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SP_PARAM_LEN],
		  { "Policy param length", "mikey.sp.param_len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},

		/* Security Policy param */
		{ &hf_mikey[POS_SP_PARAM_F],
		  { "Policy param", "mikey.sp.param",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SP_PARAM_F_TYPE],
		  { "Type", "mikey.sp.param.type",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SP_PARAM_F_LEN],
		  { "Length", "mikey.sp.param.len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SP_PARAM_F_VALUE],
		  { "Value", "mikey.sp.patam.value",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* SRTP policy param */
		{ &hf_mikey_sp_param[SP_ENCR_ALG],
		  { SP_TEXT_ENCR_ALG, "mikey.sp.encr_alg",
		    FT_UINT8, BASE_DEC, VALS(sp_encr_alg_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_ENCR_LEN],
		  { SP_TEXT_ENCR_LEN, "mikey.sp.encr_len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_AUTH_ALG],
		  { SP_TEXT_AUTH_ALG, "mikey.sp.auth_alg",
		    FT_UINT8, BASE_DEC, VALS(sp_auth_alg_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_AUTH_KEY_LEN],
		  { SP_TEXT_AUTH_KEY_LEN, "mikey.sp.auth_key_len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_SALT_LEN],
		  { SP_TEXT_SALT_LEN, "mikey.sp.salt_len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_PRF],
		  { SP_TEXT_PRF, "mikey.sp.prf",
		    FT_UINT8, BASE_DEC, VALS(sp_prf_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_KD_RATE],
		  { SP_TEXT_KD_RATE, "mikey.sp.kd_rate",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_SRTP_ENCR],
		  { SP_TEXT_SRTP_ENCR, "mikey.sp.srtp_encr",
		    FT_UINT8, BASE_DEC, VALS(on_off_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_SRTCP_ENCR],
		  { SP_TEXT_SRTCP_ENCR, "mikey.sp.srtcp_encr",
		    FT_UINT8, BASE_DEC, VALS(on_off_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_FEC],
		  { SP_TEXT_FEC, "mikey.sp.fec",
		    FT_UINT8, BASE_DEC, VALS(sp_fec_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_SRTP_AUTH],
		  { SP_TEXT_SRTP_AUTH, "mikey.sp.srtp_auth",
		    FT_UINT8, BASE_DEC, VALS(on_off_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_AUTH_TAG_LEN],
		  { SP_TEXT_AUTH_TAG_LEN, "mikey.sp.auth_tag_len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey_sp_param[SP_SRTP_PREFIX],
		  { SP_TEXT_SRTP_PREFIX, "mikey.sp.srtp_prefix",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},

		/* RAND payload (RAND) */
		{ &hf_mikey[POS_RAND_LEN],
		  { "RAND len", "mikey.rand.len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_RAND],
		  { "RAND", "mikey.rand.data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* Error payload (ERR) */
		{ &hf_mikey[POS_ERR_NO],
		  { "Error no.", "mikey.err.no",
		    FT_UINT8, BASE_DEC|BASE_EXT_STRING, &err_vals_ext, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_ERR_RESERVED],
		  { "Reserved", "mikey.err.reserved",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* IDR */
		{ &hf_mikey[POS_ID_ROLE],
		  { "ID role", "mikey.id.role",
		    FT_UINT8, BASE_DEC, VALS(id_role_vals), 0x0,
		    NULL, HFILL }},

		/* Key data sub-payload */
		{ &hf_mikey[POS_KEY_DATA_TYPE],
		  { "Type", "mikey.key.type",
		    FT_UINT8, BASE_DEC, VALS(kd_vals), 0xf0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_DATA_KV],
		  { "KV", "mikey.key.kv",
		    FT_UINT8, BASE_DEC, VALS(kv_vals), 0x0f,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_DATA_LEN],
		  { "Key len", "mikey.key.data.len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_DATA],
		  { "Key", "mikey.key.data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_SALT_LEN],
		  { "Salt key len", "mikey.key.salt.len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_SALT],
		  { "Salt key", "mikey.key.salt",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_KV_FROM_LEN],
		  { "Valid from len", "mikey.key.kv.from.len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_KV_FROM],
		  { "Valid from", "mikey.key.kv.from",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_KV_TO_LEN],
		  { "Valid to len", "mikey.key.kv.to.len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_KV_TO],
		  { "Valid to", "mikey.key.kv.to",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_KV_SPI_LEN],
		  { "Valid SPI len", "mikey.key.kv.spi.len",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_KEY_KV_SPI],
		  { "Valid SPI", "mikey.key.kv.spi",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* General Extension payload (GENERAL_EXT) */
		{ &hf_mikey[POS_GENERAL_EXT_TYPE],
		  { "Extension type", "mikey.ext.type",
		    FT_UINT8, BASE_DEC, VALS(genext_type_vals), 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_GENERAL_EXT_LEN],
		  { "Length", "mikey.ext.len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_GENERAL_EXT_DATA],
		  { "Data", "mikey.ext.data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_GENERAL_EXT_VALUE],
		  { "Value", "mikey.ext.value",
		    FT_STRING, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		/* SAKKE */
		{ &hf_mikey[POS_SAKKE_PARAMS],
		  { "SAKKE params", "mikey.sakke.params",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SAKKE_ID_SCHEME],
		  { "ID scheme", "mikey.sakke.idscheme",
		    FT_UINT8, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SAKKE_LEN],
		  { "SAKKE data length", "mikey.sakke.len",
		    FT_UINT16, BASE_DEC, NULL, 0x0,
		    NULL, HFILL }},
		{ &hf_mikey[POS_SAKKE_DATA],
		  { "SAKKE data", "mikey.sakke.data",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

/*
		{ &hf_mikey[POS_SP_PARAM],
		  { "Policy param", "mikey.policy_param",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }},

		{ &hf_mikey[POS_PAYLOAD],
		  { "Payload", "mikey.payload",
		    FT_BYTES, BASE_HEX, NULL, 0x0,
		    NULL, HFILL }},
*/
	};

	/* Setup protocol subtree array */
	static gint *ett[] = {
		&ett_mikey,
		&ett_mikey_payload,
		&ett_mikey_sp_param,
		&ett_mikey_hdr_id,
		&ett_mikey_enc_data
	};

	/* Register the protocol name and description */
	proto_mikey = proto_register_protocol("Multimedia Internet KEYing", "MIKEY", "mikey");

	mikey_handle = register_dissector("mikey", dissect_mikey, proto_mikey);

	/* Required function calls to register the header fields and subtrees used */
	proto_register_field_array(proto_mikey, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
}


void
proto_reg_handoff_mikey(void)
{
	dissector_add_string("key_mgmt", "mikey", mikey_handle);
	dissector_add_uint_with_preference("tcp.port", PORT_MIKEY, mikey_handle);
	dissector_add_uint_with_preference("udp.port", PORT_MIKEY, mikey_handle);
}
/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */
