Bläddra i källkod

jwt: new module adding json web token generation and validation

Daniel-Constantin Mierla 4 år sedan
förälder
incheckning
93691313f3

+ 25 - 0
src/modules/jwt/Makefile

@@ -0,0 +1,25 @@
+#
+# 
+# WARNING: do not run this directly, it should be run by the main Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=jwt.so
+
+ifeq ($(CROSS_COMPILE),)
+JWT_BUILDER=$(shell \
+	if pkg-config --exists libjwt; then \
+		echo 'pkg-config libjwt'; \
+	fi)
+endif
+
+ifneq ($(JWT_BUILDER),)
+	DEFS += $(shell $(JWT_BUILDER) --cflags)
+	LIBS += $(shell $(JWT_BUILDER) --libs)
+else
+	DEFS += -I$(LOCALBASE)/include
+	LIBS += -L$(LOCALBASE)/lib -ljwt
+endif
+
+include ../../Makefile.modules
+

+ 176 - 0
src/modules/jwt/README

@@ -0,0 +1,176 @@
+JWT Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2021 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. key_mode (int)
+
+        4. Functions
+
+              4.1. jwt_generate(prvkey, alg, claims)
+              4.2. jwt_verify(pubkey, alg, claims, jwtval)
+
+        5. Variables
+
+              5.1. $jwt(key)
+
+   List of Examples
+
+   1.1. Set key_mode parameter
+   1.2. jwt_generate usage
+   1.3. jwt_verify usage
+   1.4. $jwt(name) usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. key_mode (int)
+
+   4. Functions
+
+        4.1. jwt_generate(prvkey, alg, claims)
+        4.2. jwt_verify(pubkey, alg, claims, jwtval)
+
+   5. Variables
+
+        5.1. $jwt(key)
+
+1. Overview
+
+   This module provides JWT (JSON Web Token) functions to be used in
+   Kamailio configuration file.
+
+   It relies on libjwt (at least v1.12.0) library
+   (https://github.com/benmcollins/libjwt).
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * none.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * libjwt - minimum version 1.12.0.
+
+3. Parameters
+
+   3.1. key_mode (int)
+
+3.1. key_mode (int)
+
+   Mode to store the private and public keys.
+
+   Work in progress.
+
+   Default value is 0.
+
+   Example 1.1. Set key_mode parameter
+...
+modparam("jwt", "key_mode", 0)
+...
+
+4. Functions
+
+   4.1. jwt_generate(prvkey, alg, claims)
+   4.2. jwt_verify(pubkey, alg, claims, jwtval)
+
+4.1. jwt_generate(prvkey, alg, claims)
+
+   Generate the JWT, its value can be retrieved in the variable $jwt(val).
+
+   The parameters are:
+     * prvkey - path to private key
+     * alg - the algoritm to build the signature, as supported by the
+       libjwt (e.g., RS256, HS256, ES256, ...)
+     * claims - the list of claims to be added to JWT, in the format
+       "name1=value1;name2=value2;..." (same as the SIP parameters
+       format).
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.2. jwt_generate usage
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+...
+
+4.2. jwt_verify(pubkey, alg, claims, jwtval)
+
+   Verify the JWT.
+
+   The parameters are:
+     * pubkey - path to public key
+     * alg - the algoritm to build the signature, as supported by the
+       libjwt (e.g., RS256, HS256, ES256, ...)
+     * claims - the list of claims to be checked they are in the JWT, in
+       the format "name1=value1;name2=value2;..." (same as the SIP
+       parameters format).
+     * jwtval - the value of the JWT to verify
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.3. jwt_verify usage
+...
+  if(!jwt_verify("/path/to/pubkey.pem", "RS256",
+         "caller=$fU;callee=$tU;callid=$ci",
+        "$var(jwt)") {
+    xwarn("failed to verify jwt\n");
+  }
+...
+
+5. Variables
+
+   5.1. $jwt(key)
+
+5.1. $jwt(key)
+
+   Get the values and attributes after using JWT functions.
+
+   The key can be:
+     * val - the value of JWT after a successful jwt_generate().
+     * status - the status of verification after a failed jwt_verify().
+
+   Example 1.4. $jwt(name) usage
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+  xinfo("jwt is: $jwt(val)");
+...

+ 4 - 0
src/modules/jwt/doc/Makefile

@@ -0,0 +1,4 @@
+docs = jwt.xml
+
+docbook_dir = ../../../../doc/docbook
+include $(docbook_dir)/Makefile.module

+ 37 - 0
src/modules/jwt/doc/jwt.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>JWT Module</title>
+	<productname class="trade">kamailio.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2021</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <xi:include href="jwt_admin.xml"/>
+
+
+</book>

+ 218 - 0
src/modules/jwt/doc/jwt_admin.xml

@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+
+	<title>&adminguide;</title>
+
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides JWT (JSON Web Token) functions to be used
+		in &kamailio; configuration file.
+	</para>
+	<para>
+		It relies on libjwt (at least v1.12.0) library (https://github.com/benmcollins/libjwt).
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>none</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>libjwt</emphasis> - minimum version 1.12.0.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+	<title>Parameters</title>
+	<section id="jwt.p.key_mode">
+		<title><varname>key_mode</varname> (int)</title>
+		<para>
+			Mode to store the private and public keys.
+		</para>
+		<para>
+			Work in progress.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>key_mode</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("jwt", "key_mode", 0)
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+
+	<section>
+	<title>Functions</title>
+	<section id="jwt.f.jwt_generate">
+	    <title>
+		<function moreinfo="none">jwt_generate(prvkey, alg, claims)</function>
+	    </title>
+	    <para>
+	    Generate the JWT, its value can be retrieved in the variable $jwt(val).
+		</para>
+		<para>
+		The parameters are:
+		</para>
+		<itemizedlist>
+			<listitem>
+			<para>
+			prvkey - path to private key
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			alg - the algoritm to build the signature, as supported by the
+			libjwt (e.g., RS256, HS256, ES256, ...)
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			claims - the list of claims to be added to JWT, in the format
+			"name1=value1;name2=value2;..." (same as the SIP parameters format).
+			</para>
+			</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>jwt_generate</function> usage</title>
+		<programlisting format="linespecific">
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section id="jwt.f.jwt_verify">
+	    <title>
+		<function moreinfo="none">jwt_verify(pubkey, alg, claims, jwtval)</function>
+	    </title>
+	    <para>
+	    Verify the JWT.
+		</para>
+		<para>
+		The parameters are:
+		</para>
+		<itemizedlist>
+			<listitem>
+			<para>
+			pubkey - path to public key
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			alg - the algoritm to build the signature, as supported by the
+			libjwt (e.g., RS256, HS256, ES256, ...)
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			claims - the list of claims to be checked they are in the JWT, in the format
+			"name1=value1;name2=value2;..." (same as the SIP parameters format).
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			jwtval - the value of the JWT to verify
+			</para>
+			</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>jwt_verify</function> usage</title>
+		<programlisting format="linespecific">
+...
+  if(!jwt_verify("/path/to/pubkey.pem", "RS256",
+         "caller=$fU;callee=$tU;callid=$ci",
+        "$var(jwt)") {
+    xwarn("failed to verify jwt\n");
+  }
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+	<section>
+	<title>Variables</title>
+	<section id="jwt.v.jwt">
+	    <title>
+		<function moreinfo="none">$jwt(key)</function>
+	    </title>
+	    <para>
+	    Get the values and attributes after using JWT functions.
+		</para>
+		<para>
+		The key can be:
+		</para>
+		<itemizedlist>
+			<listitem>
+			<para>
+			val - the value of JWT after a successful jwt_generate().
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+			status - the status of verification after a failed jwt_verify().
+			</para>
+			</listitem>
+		</itemizedlist>
+		<example>
+		<title><function>$jwt(name)</function> usage</title>
+		<programlisting format="linespecific">
+...
+  jwt_generate("/path/to/prvkey.pem", "RS256",
+        "caller=$fU;callee=$tU;callid=$ci");
+  xinfo("jwt is: $jwt(val)");
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+
+</chapter>

+ 442 - 0
src/modules/jwt/jwt_mod.c

@@ -0,0 +1,442 @@
+/**
+ * Copyright (C) 2021 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file 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 file 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
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jwt.h>
+
+#include "../../core/sr_module.h"
+#include "../../core/dprint.h"
+#include "../../core/mod_fix.h"
+#include "../../core/lvalue.h"
+#include "../../core/kemi.h"
+#include "../../core/parser/parse_param.h"
+
+
+MODULE_VERSION
+
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+
+static int w_jwt_generate(sip_msg_t* msg, char* pkey, char* palg, char* pclaims);
+static int w_jwt_verify(sip_msg_t* msg, char* pkey, char* palg, char* pclaims,
+		char *pjwtval);
+
+static int _jwt_key_mode = 0;
+
+static str _jwt_result = STR_NULL;
+static unsigned int _jwt_verify_status = 0;
+
+static cmd_export_t cmds[]={
+	{"jwt_generate", (cmd_function)w_jwt_generate, 3,
+		fixup_spve_all, 0, ANY_ROUTE},
+	{"jwt_verify", (cmd_function)w_jwt_verify, 4,
+		fixup_spve_all, 0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{ "key_mode", PARAM_INT, &_jwt_key_mode },
+
+	{ 0, 0, 0 }
+};
+
+static int jwt_pv_get(sip_msg_t *msg, pv_param_t *param, pv_value_t *res);
+static int jwt_pv_parse_name(pv_spec_t *sp, str *in);
+static pv_export_t mod_pvs[] = {
+	{ {"jwt",  sizeof("jwt")-1}, PVT_OTHER,  jwt_pv_get,    0,
+			jwt_pv_parse_name, 0, 0, 0 },
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+struct module_exports exports = {
+	"jwt",          /* module name */
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,            /* cmd (cfg function) exports */
+	params,          /* param exports */
+	0,               /* RPC method exports */
+	mod_pvs,         /* pseudo-variables exports */
+	0,               /* response handling function */
+	mod_init,        /* module init function */
+	child_init,      /* per-child init function */
+	mod_destroy      /* module destroy function */
+};
+
+
+/**
+ * @brief Initialize crypto module function
+ */
+static int mod_init(void)
+{
+	return 0;
+}
+
+/**
+ * @brief Initialize crypto module children
+ */
+static int child_init(int rank)
+{
+	return 0;
+}
+
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+	return;
+}
+
+/**
+ *
+ */
+static int ki_jwt_generate(sip_msg_t* msg, str *key, str *alg, str *claims)
+{
+	str dupclaims = STR_NULL;
+	str sparams = STR_NULL;
+	jwt_alg_t valg = JWT_ALG_NONE;
+	time_t iat;
+	FILE *fpk = NULL;
+	unsigned char keybuf[10240];
+	size_t keybuf_len = 0;
+	param_t* params_list = NULL;
+	param_hooks_t phooks;
+	param_t *pit = NULL;
+	int ret = 0;
+	jwt_t *jwt = NULL;
+
+	if(key==NULL || key->s==NULL || alg==NULL || alg->s==NULL
+			|| claims==NULL || claims->s==NULL || claims->len<=0) {
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+	if(_jwt_result.s != NULL) {
+		jwt_free_str(_jwt_result.s);
+		_jwt_result.s = NULL;
+		_jwt_result.len = 0;
+	}
+	valg = jwt_str_alg(alg->s);
+	if (valg == JWT_ALG_INVAL) {
+		LM_ERR("not supported algorithm: %s\n", alg->s);
+		return -1;
+	}
+	if(pkg_str_dup(&dupclaims, claims)<0) {
+		LM_ERR("failed to duplicate claims\n");
+		return -1;
+	}
+	fpk= fopen(key->s, "r");
+	if(fpk==NULL) {
+		LM_ERR("failed to read key file: %s\n", key->s);
+		goto error;
+	}
+	keybuf_len = fread(keybuf, 1, sizeof(keybuf), fpk);
+	fclose(fpk);
+	if(keybuf_len==0) {
+		LM_ERR("unable to read key file content: %s\n", key->s);
+		goto error;
+	}
+	keybuf[keybuf_len] = '\0';
+	sparams = dupclaims;
+	if(sparams.s[sparams.len-1]==';') {
+		sparams.len--;
+	}
+	if (parse_params(&sparams, CLASS_ANY, &phooks, &params_list)<0) {
+		LM_ERR("failed to parse claims\n");
+		goto error;
+	}
+
+	ret = jwt_new(&jwt);
+	if (ret != 0 || jwt == NULL) {
+		LM_ERR("failed to initialize jwt\n");
+		goto error;
+	}
+
+	iat = time(NULL);
+
+	ret = jwt_add_grant_int(jwt, "iat", iat);
+	for (pit = params_list; pit; pit=pit->next) {
+		if(pit->name.len>0 && pit->body.len>0) {
+			pit->name.s[pit->name.len] = '\0';
+			pit->body.s[pit->body.len] = '\0';
+			jwt_add_grant(jwt, pit->name.s, pit->body.s);
+		}
+	}
+
+	ret = jwt_set_alg(jwt, valg, keybuf, keybuf_len);
+	if (ret < 0) {
+		LM_ERR("failed to set algorithm and key\n");
+		goto error;
+	}
+
+	_jwt_result.s = jwt_encode_str(jwt);
+	_jwt_result.len = strlen(_jwt_result.s);
+
+	free_params(params_list);
+	pkg_free(dupclaims.s);
+	jwt_free(jwt);
+
+	return 1;
+
+error:
+	if(params_list!=NULL) {
+		free_params(params_list);
+	}
+	if(dupclaims.s!=NULL) {
+		pkg_free(dupclaims.s);
+	}
+	if(jwt!=NULL) {
+		jwt_free(jwt);
+	}
+	return -1;
+}
+
+/**
+ *
+ */
+static int w_jwt_generate(sip_msg_t* msg, char* pkey, char* palg, char* pclaims)
+{
+	str skey = STR_NULL;
+	str salg = STR_NULL;
+	str sclaims = STR_NULL;
+
+	if (fixup_get_svalue(msg, (gparam_t*)pkey, &skey) != 0) {
+		LM_ERR("cannot get path to the key file\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)palg, &salg) != 0) {
+		LM_ERR("cannot get algorithm value\n");
+		return -1;
+	}
+
+	if (fixup_get_svalue(msg, (gparam_t*)pclaims, &sclaims) != 0) {
+		LM_ERR("cannot get claims value\n");
+		return -1;
+	}
+
+	return ki_jwt_generate(msg, &skey, &salg, &sclaims);
+}
+
+/**
+ *
+ */
+static int ki_jwt_verify(sip_msg_t* msg, str *key, str *alg, str *claims,
+		str *jwtval)
+{
+	str dupclaims = STR_NULL;
+	jwt_alg_t valg = JWT_ALG_NONE;
+	time_t iat;
+	FILE *fpk = NULL;
+	unsigned char keybuf[10240];
+	size_t keybuf_len = 0;
+	param_t* params_list = NULL;
+	param_hooks_t phooks;
+	param_t *pit = NULL;
+	int ret = 0;
+	jwt_t *jwt = NULL;
+	jwt_valid_t *jwt_valid = NULL;
+	str sparams = STR_NULL;
+
+	if(key==NULL || key->s==NULL || alg==NULL || alg->s==NULL
+			|| claims==NULL || claims->s==NULL || claims->len<=0
+			|| jwtval==NULL || jwtval->s==NULL || jwtval->len<=0) {
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	_jwt_verify_status = 0;
+
+	valg = jwt_str_alg(alg->s);
+	if (valg == JWT_ALG_INVAL) {
+		LM_ERR("not supported algorithm: %s\n", alg->s);
+		return -1;
+	}
+	if(pkg_str_dup(&dupclaims, claims)<0) {
+		LM_ERR("failed to duplicate claims\n");
+		return -1;
+	}
+	fpk= fopen(key->s, "r");
+	if(fpk==NULL) {
+		LM_ERR("failed to read key file: %s\n", key->s);
+		goto error;
+	}
+	keybuf_len = fread(keybuf, 1, sizeof(keybuf), fpk);
+	fclose(fpk);
+	if(keybuf_len==0) {
+		LM_ERR("unable to read key file content: %s\n", key->s);
+		goto error;
+	}
+	keybuf[keybuf_len] = '\0';
+	sparams = dupclaims;
+	if(sparams.s[sparams.len-1]==';') {
+		sparams.len--;
+	}
+	if (parse_params(&sparams, CLASS_ANY, &phooks, &params_list)<0) {
+		LM_ERR("failed to parse claims\n");
+		goto error;
+	}
+
+	ret = jwt_valid_new(&jwt_valid, valg);
+	if (ret != 0 || jwt_valid == NULL) {
+		LM_ERR("failed to initialize jwt valid\n");
+		goto error;
+	}
+
+	iat = time(NULL);
+	jwt_valid_set_headers(jwt_valid, 1);
+	jwt_valid_set_now(jwt_valid, iat);
+
+	for (pit = params_list; pit; pit=pit->next) {
+		if(pit->name.len>0 && pit->body.len>0) {
+			pit->name.s[pit->name.len] = '\0';
+			pit->body.s[pit->body.len] = '\0';
+			jwt_valid_add_grant(jwt_valid, pit->name.s, pit->body.s);
+		}
+	}
+
+	ret = jwt_decode(&jwt, jwtval->s, keybuf, keybuf_len);
+	if (ret!=0 || jwt==NULL) {
+		LM_ERR("failed to decode jwt value\n");
+		goto error;
+	}
+	if (jwt_validate(jwt, jwt_valid) != 0) {
+		_jwt_verify_status = jwt_valid_get_status(jwt_valid);
+		LM_ERR("failed to validate jwt: %08x\n", _jwt_verify_status);
+		goto error;
+	}
+
+	free_params(params_list);
+	pkg_free(dupclaims.s);
+	jwt_free(jwt);
+	jwt_valid_free(jwt_valid);
+
+	return 1;
+
+error:
+	if(params_list!=NULL) {
+		free_params(params_list);
+	}
+	if(dupclaims.s!=NULL) {
+		pkg_free(dupclaims.s);
+	}
+	if(jwt!=NULL) {
+		jwt_free(jwt);
+	}
+	if(jwt_valid!=NULL) {
+		jwt_valid_free(jwt_valid);
+	}
+	return -1;
+}
+
+/**
+ *
+ */
+static int w_jwt_verify(sip_msg_t* msg, char* pkey, char* palg, char* pclaims,
+		char *pjwtval)
+{
+	str skey = STR_NULL;
+	str salg = STR_NULL;
+	str sclaims = STR_NULL;
+	str sjwtval = STR_NULL;
+
+	if (fixup_get_svalue(msg, (gparam_t*)pkey, &skey) != 0) {
+		LM_ERR("cannot get path to the key file\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)palg, &salg) != 0) {
+		LM_ERR("cannot get algorithm value\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)pclaims, &sclaims) != 0) {
+		LM_ERR("cannot get claims value\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)pjwtval, &sjwtval) != 0) {
+		LM_ERR("cannot get jwt value\n");
+		return -1;
+	}
+
+	return ki_jwt_verify(msg, &skey, &salg, &sclaims, &sjwtval);
+}
+
+/**
+ *
+ */
+static int jwt_pv_get(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
+{
+	switch(param->pvn.u.isname.name.n)
+	{
+		case 0:
+			if(_jwt_result.s==NULL)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res, &_jwt_result);
+		case 1:
+			return pv_get_uintval(msg, param, res, _jwt_verify_status);
+		default:
+			return pv_get_null(msg, param, res);
+	}
+}
+
+/**
+ *
+ */
+static int jwt_pv_parse_name(pv_spec_t *sp, str *in)
+{
+	if(in->len==3 && strncmp(in->s, "val", 3)==0) {
+		sp->pvp.pvn.u.isname.name.n = 0;
+	} else if(in->len==6 && strncmp(in->s, "status", 6)==0) {
+		sp->pvp.pvn.u.isname.name.n = 1;
+	} else {
+		LM_ERR("unknown inner name [%.*s]\n", in->len, in->s);
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+/* clang-format off */
+static sr_kemi_t sr_kemi_jwt_exports[] = {
+	{ str_init("jwt"), str_init("jwt_generate"),
+		SR_KEMIP_INT, ki_jwt_generate,
+		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
+			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
+	},
+	{ str_init("jwt"), str_init("jwt_verify"),
+		SR_KEMIP_INT, ki_jwt_verify,
+		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
+			SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE }
+	},
+
+	{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
+};
+/* clang-format on */
+
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+	sr_kemi_modules_add(sr_kemi_jwt_exports);
+	return 0;
+}