summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-10-10 11:37:56 +0000
committerBenny Prijono <bennylp@teluu.com>2007-10-10 11:37:56 +0000
commita3354959d1493f86392296ab0e78512e633abf40 (patch)
tree8e73a0994d92c5b97d77cf43165f2bc84d5313a3 /pjsip
parent0cb2d5b9e84601bb822a300c48a4ea8397d85850 (diff)
Ticket #396: initial implementation of digest AKA (akav1-md5) authentication for IMS/3GPP
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1488 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/build/pjsip_core.dsp8
-rw-r--r--pjsip/include/pjsip.h1
-rw-r--r--pjsip/include/pjsip/sip_auth.h103
-rw-r--r--pjsip/include/pjsip/sip_auth_aka.h91
-rw-r--r--pjsip/include/pjsip/sip_errno.h10
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h16
-rw-r--r--pjsip/src/pjsip/sip_auth_aka.c121
-rw-r--r--pjsip/src/pjsip/sip_auth_client.c103
-rw-r--r--pjsip/src/pjsip/sip_errno.c2
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c10
10 files changed, 409 insertions, 56 deletions
diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp
index 2cd4fc06..245985a2 100644
--- a/pjsip/build/pjsip_core.dsp
+++ b/pjsip/build/pjsip_core.dsp
@@ -164,6 +164,10 @@ SOURCE=..\src\pjsip\sip_transport_udp.c
# PROP Default_Filter ""
# Begin Source File
+SOURCE=..\src\pjsip\sip_auth_aka.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjsip\sip_auth_client.c
# End Source File
# Begin Source File
@@ -308,6 +312,10 @@ SOURCE=..\include\pjsip\sip_auth.h
# End Source File
# Begin Source File
+SOURCE=..\include\pjsip\sip_auth_aka.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\pjsip\sip_auth_msg.h
# End Source File
# Begin Source File
diff --git a/pjsip/include/pjsip.h b/pjsip/include/pjsip.h
index 867bb1e5..3e9b4137 100644
--- a/pjsip/include/pjsip.h
+++ b/pjsip/include/pjsip.h
@@ -45,6 +45,7 @@
/* Authentication. */
#include <pjsip/sip_auth.h>
+#include <pjsip/sip_auth_aka.h>
/* Transaction layer. */
#include <pjsip/sip_transaction.h>
diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h
index 1ae6c1f6..521adfaa 100644
--- a/pjsip/include/pjsip/sip_auth.h
+++ b/pjsip/include/pjsip/sip_auth.h
@@ -46,11 +46,14 @@ PJ_BEGIN_DECL
#define PJSIP_MD5STRLEN 32
-/** Type of data in the credential information. */
+/** Type of data in the credential information in #pjsip_cred_info. */
typedef enum pjsip_cred_data_type
{
- PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */
- PJSIP_CRED_DATA_DIGEST /**< Hashed digest. */
+ PJSIP_CRED_DATA_PLAIN_PASSWD=0, /**< Plain text password. */
+ PJSIP_CRED_DATA_DIGEST =1, /**< Hashed digest. */
+
+ PJSIP_CRED_DATA_EXT_AKA =16 /**< Extended AKA info is available */
+
} pjsip_cred_data_type;
/** Authentication's quality of protection (qop) type. */
@@ -63,13 +66,43 @@ typedef enum pjsip_auth_qop_type
} pjsip_auth_qop_type;
+/**
+ * Type of callback function to create authentication response.
+ * Application can specify this callback in \a cb field of the credential info
+ * (#pjsip_cred_info) and specifying PJSIP_CRED_DATA_DIGEST_CALLBACK as
+ * \a data_type. When this function is called, most of the fields in the
+ * \a auth authentication response will have been filled by the framework.
+ * Application normally should just need to calculate the response digest
+ * of the authentication response.
+ *
+ * @param pool Pool to allocate memory from if application needs to.
+ * @param chal The authentication challenge sent by server in 401
+ * or 401 response, in either Proxy-Authenticate or
+ * WWW-Authenticate header.
+ * @param cred The credential that has been selected by the framework
+ * to authenticate against the challenge.
+ * @param auth The authentication response which application needs to
+ * calculate the response digest.
+ *
+ * @return Application may return non-PJ_SUCCESS to abort the
+ * authentication process. When this happens, the
+ * framework will return failure to the original function
+ * that requested authentication.
+ */
+typedef pj_status_t (*pjsip_cred_cb)(pj_pool_t *pool,
+ const pjsip_digest_challenge *chal,
+ const pjsip_cred_info *cred,
+ const pj_str_t *method,
+ pjsip_digest_credential *auth);
+
+
/**
* This structure describes credential information.
* A credential information is a static, persistent information that identifies
* username and password required to authorize to a specific realm.
*
* Note that since PJSIP 0.7.0.1, it is possible to make a credential that is
- * valid for any realms, by setting the realm to star/asterisk character,
+ * valid for any realms, by setting the realm to star/wildcard character,
* i.e. realm = pj_str("*");.
*/
struct pjsip_cred_info
@@ -82,6 +115,21 @@ struct pjsip_cred_info
int data_type; /**< Type of data (0 for plaintext passwd). */
pj_str_t data; /**< The data, which can be a plaintext
password or a hashed digest. */
+
+ /** Extended data */
+ union {
+ /** Digest AKA credential information. Note that when AKA credential
+ * is being used, the \a data field of this #pjsip_cred_info is
+ * not used, but it still must be initialized to an empty string.
+ */
+ struct {
+ pj_str_t k; /**< Permanent key. */
+ pj_str_t op; /**< Operator variant key. */
+ pj_str_t amf; /**< Authentication Management Field */
+ pjsip_cred_cb cb; /**< Callback to create AKA digest. */
+ } aka;
+
+ } ext;
};
/**
@@ -150,6 +198,17 @@ typedef struct pjsip_auth_clt_sess
/**
+ * Duplicate a credential info.
+ *
+ * @param pool The memory pool.
+ * @param dst Destination credential.
+ * @param src Source credential.
+ */
+PJ_DECL(void) pjsip_cred_info_dup(pj_pool_t *pool,
+ pjsip_cred_info *dst,
+ const pjsip_cred_info *src);
+
+/**
* Type of function to lookup credential for the specified name.
*
* @param pool Pool to initialize the credential info.
@@ -349,22 +408,36 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv,
pj_bool_t stale,
pjsip_tx_data *tdata);
+/**
+ * Helper function to create MD5 digest out of the specified
+ * parameters.
+ *
+ * @param result String to store the response digest. This string
+ * must have been preallocated by caller with the
+ * buffer at least PJSIP_MD5STRLEN (32 bytes) in size.
+ * @param nonce Optional nonce.
+ * @param nc Nonce count.
+ * @param cnonce Optional cnonce.
+ * @param qop Optional qop.
+ * @param uri URI.
+ * @param realm Realm.
+ * @param cred_info Credential info.
+ * @param method SIP method.
+ */
+PJ_DECL(void) pjsip_auth_create_digest(pj_str_t *result,
+ const pj_str_t *nonce,
+ const pj_str_t *nc,
+ const pj_str_t *cnonce,
+ const pj_str_t *qop,
+ const pj_str_t *uri,
+ const pj_str_t *realm,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *method);
/**
* @}
*/
-/* Internal function defined in sip_auth_client.c */
-void pjsip_auth_create_digest( pj_str_t *result,
- const pj_str_t *nonce,
- const pj_str_t *nc,
- const pj_str_t *cnonce,
- const pj_str_t *qop,
- const pj_str_t *uri,
- const pj_str_t *realm,
- const pjsip_cred_info *cred_info,
- const pj_str_t *method);
-
PJ_END_DECL
diff --git a/pjsip/include/pjsip/sip_auth_aka.h b/pjsip/include/pjsip/sip_auth_aka.h
new file mode 100644
index 00000000..c5c2a60d
--- /dev/null
+++ b/pjsip/include/pjsip/sip_auth_aka.h
@@ -0,0 +1,91 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_AKA_H__
+#define __PJSIP_AUTH_SIP_AUTH_AKA_H__
+
+/**
+ * @file sip_auth_aka.h
+ * @brief SIP Digest AKA Authorization Module.
+ */
+
+#include <pjsip/sip_auth.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_AUTH_AKA_API Digest AKA Authentication API's
+ * @ingroup PJSIP_AUTH_API
+ * @brief Digest AKA helper API.
+ * @{
+ *
+ * This module currently exports one function, #pjsip_auth_create_akav1_response(),
+ * which can be registered as the callback function in \a ext.aka.cb field
+ * of #pjsip_cred_info structure, to calculate the MD5-AKAv1 digest
+ * response.
+ */
+
+
+#define PJSIP_AKA_AKLEN 6
+#define PJSIP_AKA_AMFLEN 2
+#define PJSIP_AKA_AUTNLEN 16
+#define PJSIP_AKA_CKLEN 16
+#define PJSIP_AKA_IKLEN 16
+#define PJSIP_AKA_KLEN 16
+#define PJSIP_AKA_OPLEN 16
+#define PJSIP_AKA_RANDLEN 16
+#define PJSIP_AKA_RESLEN 8
+#define PJSIP_AKA_MACLEN 8
+
+/**
+ * This function creates MD5 AKAv1 response for the specified challenge
+ * in \a chal, based on the information in the credential \a cred.
+ * Application may register this function as \a ext.aka.cb field of
+ * #pjsip_cred_info structure to make PJSIP automatically call this
+ * function to calculate the response digest.
+ *
+ * @param pool Pool to allocate memory.
+ * @param chal The authentication challenge sent by server in 401
+ * or 401 response, in either Proxy-Authenticate or
+ * WWW-Authenticate header.
+ * @param cred The credential that has been selected by the framework
+ * to authenticate against the challenge.
+ * @param method The request method.
+ * @param auth The authentication credential where the digest response
+ * will be placed to.
+ *
+ * @return PJ_SUCCESS if response has been created successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_auth_create_akav1(pj_pool_t *pool,
+ const pjsip_digest_challenge*chal,
+ const pjsip_cred_info *cred,
+ const pj_str_t *method,
+ pjsip_digest_credential *auth);
+
+
+/**
+ * @}
+ */
+
+
+
+PJ_END_DECL
+
+
+#endif /* __PJSIP_AUTH_SIP_AUTH_AKA_H__ */
+
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index 0e5ae2f1..0b793a9c 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -381,6 +381,16 @@ PJ_BEGIN_DECL
* keeps rejecting our authorization request with stale=true.
*/
#define PJSIP_EAUTHSTALECOUNT (PJSIP_ERRNO_START_PJSIP + 111) /* 171111 */
+/**
+ * @hideinitializer
+ * Invalid nonce value in the challenge.
+ */
+#define PJSIP_EAUTHINNONCE (PJSIP_ERRNO_START_PJSIP + 112) /* 171112 */
+/**
+ * @hideinitializer
+ * Invalid AKA credential.
+ */
+#define PJSIP_EAUTHINAKACRED (PJSIP_ERRNO_START_PJSIP + 113) /* 171113 */
/************************************************************
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index fad79178..91856d2c 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -1029,20 +1029,8 @@ typedef struct pjsua_config
PJ_DECL(void) pjsua_config_default(pjsua_config *cfg);
-/**
- * Duplicate credential.
- *
- * @param pool The memory pool.
- * @param dst Destination credential.
- * @param src Source credential.
- *
- * \par Python:
- * Not applicable (for now). Probably we could just assign one credential
- * variable to another, but this has not been tested.
- */
-PJ_DECL(void) pjsip_cred_dup( pj_pool_t *pool,
- pjsip_cred_info *dst,
- const pjsip_cred_info *src);
+/* The implementation has been moved to sip_auth.h */
+#define pjsip_cred_dup pjsip_cred_info_dup
/**
diff --git a/pjsip/src/pjsip/sip_auth_aka.c b/pjsip/src/pjsip/sip_auth_aka.c
new file mode 100644
index 00000000..b88ddb7c
--- /dev/null
+++ b/pjsip/src/pjsip/sip_auth_aka.c
@@ -0,0 +1,121 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_auth_aka.h>
+#include <pjsip/sip_errno.h>
+#include <pjlib-util/base64.h>
+#include <pjlib-util/md5.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#include "../../third_party/milenage/milenage.h"
+
+/*
+ * Create MD5-AKA1 digest response.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_create_akav1( pj_pool_t *pool,
+ const pjsip_digest_challenge*chal,
+ const pjsip_cred_info *cred,
+ const pj_str_t *method,
+ pjsip_digest_credential *auth)
+{
+ pj_str_t nonce_bin;
+ pj_uint8_t *chal_rand, *chal_autn, *chal_mac;
+ pj_uint8_t res[PJSIP_AKA_RESLEN];
+ pj_uint8_t ck[PJSIP_AKA_CKLEN];
+ pj_uint8_t ik[PJSIP_AKA_IKLEN];
+ pj_uint8_t ak[PJSIP_AKA_AKLEN];
+ pj_uint8_t sqn[PJSIP_AKA_AUTNLEN];
+ pj_uint8_t xmac[PJSIP_AKA_MACLEN];
+ pjsip_cred_info aka_cred;
+ int i;
+ pj_status_t status;
+
+ /* Check the algorithm is supported. */
+ if (pj_stricmp2(&chal->algorithm, "md5") == 0) {
+ pjsip_auth_create_digest(&auth->response, &auth->nonce, &auth->nc,
+ &auth->cnonce, &auth->qop, &auth->uri,
+ &auth->realm, cred, method);
+ return PJ_SUCCESS;
+
+ } else if (pj_stricmp2(&chal->algorithm, "AKAv1-MD5") != 0) {
+ /* Unsupported algorithm */
+ return PJSIP_EINVALIDALGORITHM;
+ }
+
+ /* Decode nonce */
+ nonce_bin.slen = PJ_BASE64_TO_BASE256_LEN(chal->nonce.slen);
+ nonce_bin.ptr = pj_pool_alloc(pool, nonce_bin.slen + 1);
+ status = pj_base64_decode(&chal->nonce, (pj_uint8_t*)nonce_bin.ptr,
+ &nonce_bin.slen);
+ if (status != PJ_SUCCESS)
+ return PJSIP_EAUTHINNONCE;
+
+ if (nonce_bin.slen < PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN + PJSIP_AKA_MACLEN)
+ return PJSIP_EAUTHINNONCE;
+
+ /* Get RAND, AUTN, and MAC */
+ chal_rand = (pj_uint8_t*) (nonce_bin.ptr + 0);
+ chal_autn = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN);
+ chal_mac = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN);
+
+ /* Verify credential */
+ PJ_ASSERT_RETURN(cred->ext.aka.k.slen == PJSIP_AKA_KLEN, PJSIP_EAUTHINAKACRED);
+ PJ_ASSERT_RETURN(cred->ext.aka.op.slen == PJSIP_AKA_OPLEN, PJSIP_EAUTHINAKACRED);
+
+ /* Given key K and random challenge RAND, compute response RES,
+ * confidentiality key CK, integrity key IK and anonymity key AK.
+ */
+ f2345((pj_uint8_t*)cred->ext.aka.k.ptr,
+ chal_rand,
+ res, ck, ik, ak,
+ (pj_uint8_t*)cred->ext.aka.op.ptr);
+
+ /* Compute sequence number SQN */
+ for (i=0; i<PJSIP_AKA_AUTNLEN; ++i)
+ sqn[i] = (pj_uint8_t) (chal_autn[i] ^ ak[i]);
+
+ /* Compute XMAC */
+ f1((pj_uint8_t*)cred->ext.aka.k.ptr, chal_rand, sqn,
+ (pj_uint8_t*)cred->ext.aka.amf.ptr, xmac,
+ (pj_uint8_t*)cred->ext.aka.op.ptr);
+
+ /* Verify MAC in the challenge */
+ if (pj_memcmp(chal_mac, xmac, PJSIP_AKA_MACLEN) != 0) {
+ return PJSIP_EAUTHINNONCE;
+ }
+
+ /* Build a temporary credential info to create MD5 digest, using
+ * "res" as the password.
+ */
+ pj_memcpy(&aka_cred, cred, sizeof(aka_cred));
+ aka_cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
+ aka_cred.data.ptr = (char*)res;
+ aka_cred.data.slen = PJSIP_AKA_RESLEN;
+
+ /* Create a response */
+ pjsip_auth_create_digest(&auth->response, &chal->nonce,
+ &auth->nc, &auth->cnonce, &auth->qop, &auth->uri,
+ &chal->realm, &aka_cred, method);
+
+ /* Done */
+ return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
index 7978da27..88cd3e94 100644
--- a/pjsip/src/pjsip/sip_auth_client.c
+++ b/pjsip/src/pjsip/sip_auth_client.c
@@ -19,6 +19,7 @@
#include <pjsip/sip_auth.h>
#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */
+#include <pjsip/sip_auth_aka.h>
#include <pjsip/sip_transport.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_errno.h>
@@ -43,6 +44,28 @@
# define AUTH_TRACE_(expr)
#endif
+#define PASSWD_MASK 0x000F
+#define EXT_MASK 0x00F0
+
+
+PJ_DEF(void) pjsip_cred_info_dup(pj_pool_t *pool,
+ pjsip_cred_info *dst,
+ const pjsip_cred_info *src)
+{
+ pj_memcpy(dst, src, sizeof(pjsip_cred_info));
+
+ pj_strdup_with_null(pool, &dst->realm, &src->realm);
+ pj_strdup_with_null(pool, &dst->scheme, &src->scheme);
+ pj_strdup_with_null(pool, &dst->username, &src->username);
+ pj_strdup_with_null(pool, &dst->data, &src->data);
+
+ if ((dst->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) {
+ pj_strdup(pool, &dst->ext.aka.k, &src->ext.aka.k);
+ pj_strdup(pool, &dst->ext.aka.op, &src->ext.aka.op);
+ pj_strdup(pool, &dst->ext.aka.amf, &src->ext.aka.amf);
+ }
+}
+
/* Transform digest to string.
* output must be at least PJSIP_MD5STRLEN+1 bytes.
@@ -63,15 +86,15 @@ static void digest2str(const unsigned char digest[], char *output)
* Create response digest based on the parameters and store the
* digest ASCII in 'result'.
*/
-void pjsip_auth_create_digest( pj_str_t *result,
- const pj_str_t *nonce,
- const pj_str_t *nc,
- const pj_str_t *cnonce,
- const pj_str_t *qop,
- const pj_str_t *uri,
- const pj_str_t *realm,
- const pjsip_cred_info *cred_info,
- const pj_str_t *method)
+PJ_DEF(void) pjsip_auth_create_digest( pj_str_t *result,
+ const pj_str_t *nonce,
+ const pj_str_t *nc,
+ const pj_str_t *cnonce,
+ const pj_str_t *qop,
+ const pj_str_t *uri,
+ const pj_str_t *realm,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *method)
{
char ha1[PJSIP_MD5STRLEN];
char ha2[PJSIP_MD5STRLEN];
@@ -82,7 +105,7 @@ void pjsip_auth_create_digest( pj_str_t *result,
AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
- if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {
+ if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_PLAIN_PASSWD) {
/***
*** ha1 = MD5(username ":" realm ":" password)
***/
@@ -96,9 +119,11 @@ void pjsip_auth_create_digest( pj_str_t *result,
digest2str(digest, ha1);
- } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
+ } else if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_DIGEST) {
pj_assert(cred_info->data.slen == 32);
pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );
+ } else {
+ pj_assert(!"Invalid data_type");
}
AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1));
@@ -220,9 +245,17 @@ static pj_status_t respond_digest( pj_pool_t *pool,
if (chal->qop.slen == 0) {
/* Server doesn't require quality of protection. */
- /* Convert digest to string and store in chal->response. */
- pjsip_auth_create_digest( &cred->response, &cred->nonce, NULL, NULL,
- NULL, uri, &chal->realm, cred_info, method);
+ if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) {
+ /* Call application callback to create the response digest */
+ return (*cred_info->ext.aka.cb)(pool, chal, cred_info,
+ method, cred);
+ }
+ else {
+ /* Convert digest to string and store in chal->response. */
+ pjsip_auth_create_digest( &cred->response, &cred->nonce, NULL,
+ NULL, NULL, uri, &chal->realm,
+ cred_info, method);
+ }
} else if (has_auth_qop(pool, &chal->qop)) {
/* Server requires quality of protection.
@@ -239,9 +272,16 @@ static pj_status_t respond_digest( pj_pool_t *pool,
pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
}
- pjsip_auth_create_digest( &cred->response, &cred->nonce, &cred->nc,
- cnonce, &pjsip_AUTH_STR, uri, &chal->realm,
- cred_info, method );
+ if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) {
+ /* Call application callback to create the response digest */
+ return (*cred_info->ext.aka.cb)(pool, chal, cred_info,
+ method, cred);
+ }
+ else {
+ pjsip_auth_create_digest( &cred->response, &cred->nonce,
+ &cred->nc, cnonce, &pjsip_AUTH_STR,
+ uri, &chal->realm, cred_info, method );
+ }
} else {
/* Server requires quality protection that we don't support. */
@@ -424,6 +464,35 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,
pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c));
for (i=0; i<cred_cnt; ++i) {
sess->cred_info[i].data_type = c[i].data_type;
+
+ /* When data_type is PJSIP_CRED_DATA_EXT_AKA,
+ * callback must be specified.
+ */
+ if ((c[i].data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) {
+ /* Callback must be specified */
+ PJ_ASSERT_RETURN(c[i].ext.aka.cb != NULL, PJ_EINVAL);
+
+ /* Verify K len */
+ PJ_ASSERT_RETURN(c[i].ext.aka.k.slen == PJSIP_AKA_KLEN,
+ PJSIP_EAUTHINAKACRED);
+
+ /* Verify OP len */
+ PJ_ASSERT_RETURN(c[i].ext.aka.op.slen == PJSIP_AKA_OPLEN,
+ PJSIP_EAUTHINAKACRED);
+
+ /* Verify AMF len */
+ PJ_ASSERT_RETURN(c[i].ext.aka.amf.slen == PJSIP_AKA_AMFLEN,
+ PJSIP_EAUTHINAKACRED);
+
+ sess->cred_info[i].ext.aka.cb = c[i].ext.aka.cb;
+ pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.k,
+ &c[i].ext.aka.k);
+ pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.op,
+ &c[i].ext.aka.op);
+ pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.amf,
+ &c[i].ext.aka.amf);
+ }
+
pj_strdup(sess->pool, &sess->cred_info[i].scheme, &c[i].scheme);
pj_strdup(sess->pool, &sess->cred_info[i].realm, &c[i].realm);
pj_strdup(sess->pool, &sess->cred_info[i].username, &c[i].username);
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index dd2724e9..2a6e9306 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -102,6 +102,8 @@ static const struct
PJ_BUILD_ERR( PJSIP_EAUTHINVALIDREALM, "Invalid authorization realm"),
PJ_BUILD_ERR( PJSIP_EAUTHINVALIDDIGEST,"Invalid authorization digest" ),
PJ_BUILD_ERR( PJSIP_EAUTHSTALECOUNT, "Maximum number of stale retries exceeded"),
+ PJ_BUILD_ERR( PJSIP_EAUTHINNONCE, "Invalid nonce value in authentication challenge"),
+ PJ_BUILD_ERR( PJSIP_EAUTHINAKACRED, "Invalid AKA credential"),
/* UA/dialog layer. */
PJ_BUILD_ERR( PJSIP_EMISSINGTAG, "Missing From/To tag parameter" ),
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 80617605..29cee191 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -81,16 +81,6 @@ PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
cfg->thread_cnt = 1;
}
-PJ_DEF(void) pjsip_cred_dup( pj_pool_t *pool,
- pjsip_cred_info *dst,
- const pjsip_cred_info *src)
-{
- pj_strdup_with_null(pool, &dst->realm, &src->realm);
- pj_strdup_with_null(pool, &dst->scheme, &src->scheme);
- pj_strdup_with_null(pool, &dst->username, &src->username);
- pj_strdup_with_null(pool, &dst->data, &src->data);
-}
-
PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
pjsua_config *dst,
const pjsua_config *src)