1
/* $Id: sip_auth_aka.c 2394 2008-12-23 17:27:53Z bennylp $ */
3
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
* Additional permission under GNU GPL version 3 section 7:
22
* If you modify this program, or any covered work, by linking or
23
* combining it with the OpenSSL project's OpenSSL library (or a
24
* modified version of that library), containing parts covered by the
25
* terms of the OpenSSL or SSLeay licenses, Teluu Inc. (http://www.teluu.com)
26
* grants you additional permission to convey the resulting work.
27
* Corresponding Source for a non-source form of such a combination
28
* shall include the source code for the parts of OpenSSL used as well
29
* as that of the covered work.
31
#include <pjsip/sip_auth_aka.h>
32
#include <pjsip/sip_errno.h>
33
#include <pjlib-util/base64.h>
34
#include <pjlib-util/md5.h>
35
#include <pjlib-util/hmac_md5.h>
36
#include <pj/assert.h>
39
#include <pj/string.h>
41
#if PJSIP_HAS_DIGEST_AKA_AUTH
43
#include "../../third_party/milenage/milenage.h"
46
* Create MD5-AKA1 digest response.
48
PJ_DEF(pj_status_t) pjsip_auth_create_aka_response(
50
const pjsip_digest_challenge*chal,
51
const pjsip_cred_info *cred,
52
const pj_str_t *method,
53
pjsip_digest_credential *auth)
57
const pj_str_t pjsip_AKAv1_MD5 = { "AKAv1-MD5", 9 };
58
const pj_str_t pjsip_AKAv2_MD5 = { "AKAv2-MD5", 9 };
59
pj_uint8_t *chal_rand, *chal_sqnxoraka, *chal_mac;
60
pj_uint8_t k[PJSIP_AKA_KLEN];
61
pj_uint8_t op[PJSIP_AKA_OPLEN];
62
pj_uint8_t amf[PJSIP_AKA_AMFLEN];
63
pj_uint8_t res[PJSIP_AKA_RESLEN];
64
pj_uint8_t ck[PJSIP_AKA_CKLEN];
65
pj_uint8_t ik[PJSIP_AKA_IKLEN];
66
pj_uint8_t ak[PJSIP_AKA_AKLEN];
67
pj_uint8_t sqn[PJSIP_AKA_SQNLEN];
68
pj_uint8_t xmac[PJSIP_AKA_MACLEN];
69
pjsip_cred_info aka_cred;
73
/* Check the algorithm is supported. */
74
if (chal->algorithm.slen==0 || pj_stricmp2(&chal->algorithm, "md5") == 0) {
76
* A normal MD5 authentication is requested. Fallbackt to the usual
77
* MD5 digest creation.
79
pjsip_auth_create_digest(&auth->response, &auth->nonce, &auth->nc,
80
&auth->cnonce, &auth->qop, &auth->uri,
81
&auth->realm, cred, method);
84
} else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5) == 0) {
86
* AKA version 1 is requested.
90
} else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv2_MD5) == 0) {
92
* AKA version 2 is requested.
97
/* Unsupported algorithm */
98
return PJSIP_EINVALIDALGORITHM;
102
nonce_bin.slen = len = PJ_BASE64_TO_BASE256_LEN(chal->nonce.slen);
103
nonce_bin.ptr = pj_pool_alloc(pool, nonce_bin.slen + 1);
104
status = pj_base64_decode(&chal->nonce, (pj_uint8_t*)nonce_bin.ptr, &len);
105
nonce_bin.slen = len;
106
if (status != PJ_SUCCESS)
107
return PJSIP_EAUTHINNONCE;
109
if (nonce_bin.slen < PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN)
110
return PJSIP_EAUTHINNONCE;
112
/* Get RAND, AUTN, and MAC */
113
chal_rand = (pj_uint8_t*)(nonce_bin.ptr + 0);
114
chal_sqnxoraka = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN);
115
chal_mac = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN +
116
PJSIP_AKA_SQNLEN + PJSIP_AKA_AMFLEN);
118
/* Copy k. op, and amf */
119
pj_bzero(k, sizeof(k));
120
pj_bzero(op, sizeof(op));
121
pj_bzero(amf, sizeof(amf));
123
if (cred->ext.aka.k.slen)
124
pj_memcpy(k, cred->ext.aka.k.ptr, cred->ext.aka.k.slen);
125
if (cred->ext.aka.op.slen)
126
pj_memcpy(op, cred->ext.aka.op.ptr, cred->ext.aka.op.slen);
127
if (cred->ext.aka.amf.slen)
128
pj_memcpy(amf, cred->ext.aka.amf.ptr, cred->ext.aka.amf.slen);
130
/* Given key K and random challenge RAND, compute response RES,
131
* confidentiality key CK, integrity key IK and anonymity key AK.
133
f2345(k, chal_rand, res, ck, ik, ak, op);
135
/* Compute sequence number SQN */
136
for (i=0; i<PJSIP_AKA_SQNLEN; ++i)
137
sqn[i] = (pj_uint8_t) (chal_sqnxoraka[i] ^ ak[i]);
139
/* Verify MAC in the challenge */
141
f1(k, chal_rand, sqn, amf, xmac, op);
143
if (pj_memcmp(chal_mac, xmac, PJSIP_AKA_MACLEN) != 0) {
144
return PJSIP_EAUTHINNONCE;
147
/* Build a temporary credential info to create MD5 digest, using
148
* "res" as the password.
150
pj_memcpy(&aka_cred, cred, sizeof(aka_cred));
151
aka_cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
153
/* Create a response */
154
if (aka_version == 1) {
156
* For AKAv1, the password is RES
158
aka_cred.data.ptr = (char*)res;
159
aka_cred.data.slen = PJSIP_AKA_RESLEN;
161
pjsip_auth_create_digest(&auth->response, &chal->nonce,
162
&auth->nc, &auth->cnonce, &auth->qop,
163
&auth->uri, &chal->realm, &aka_cred, method);
165
} else if (aka_version == 2) {
167
* For AKAv2, password is base64 encoded [1] parameters:
168
* PRF(RES||IK||CK,"http-digest-akav2-password")
170
* The pseudo-random function (PRF) is HMAC-MD5 in this case.
172
* Hmmm.. but those above doesn't seem to work, and this below does!
174
aka_cred.data.slen = PJSIP_AKA_RESLEN + PJSIP_AKA_IKLEN +
176
aka_cred.data.ptr = pj_pool_alloc(pool, aka_cred.data.slen);
178
pj_memcpy(aka_cred.data.ptr + 0, res, PJSIP_AKA_RESLEN);
179
pj_memcpy(aka_cred.data.ptr + PJSIP_AKA_RESLEN, ik, PJSIP_AKA_IKLEN);
180
pj_memcpy(aka_cred.data.ptr + PJSIP_AKA_RESLEN + PJSIP_AKA_IKLEN,
181
ck, PJSIP_AKA_CKLEN);
183
pjsip_auth_create_digest(&auth->response, &chal->nonce,
184
&auth->nc, &auth->cnonce, &auth->qop,
185
&auth->uri, &chal->realm, &aka_cred, method);
197
#endif /* PJSIP_HAS_DIGEST_AKA_AUTH */