Hi all,
I wasn't able to perform Digest-AKAv1-MD5 authentication with current
sipp code from sourceforge. I tried to repair it and found something
that works. I suggest here the changes if any of the people who mantain
sipp want to commit them or if someone wants to use Digest-AKAv1-MD5. I
also attach my auth.cpp just in case someone wants a quick and dirty
hack to get it through (the file includes some ugly logging I introduced
when trying to debug the situation).
I guess these issues have been introduced because the way the parameters
from the scenario file have changed or something similar.
In the file src/auth.cpp I identify following issues:
1) line 663 where it calls Milenage f1 function f1(k,rnd,sqn,(unsigned
char *) aka_AMF,xmac,op);
it should have been f1(k,rnd,sqn,amf,xmac,op); because aka_AMF is
actually a character string and its amf variable defined as u_char *
(uint8_t?) the one that would be correct there.
2) lines 650,651,653 read like this
memcpy(k,aka_K,KLEN);
memcpy(amf,aka_AMF,AMFLEN);
memcpy(op,aka_OP,OPLEN);
But aka_K, aka_AMF, aka_OP are character strings read from the scenario
Authentication field parameters and in k, amf, op we need byte arrays to
pass them to the Milenage functions.
I introduced some hex2uint function suggested by some stackoverflow
people to resolve the issue and convert between the two things and then
just called that instead of doing direct memcpy.
After solving those two issues Digest-AKAv1-MD5 authentication worked.
In my case because I didn't introduce many safe checks into the code it
was fundamental that I had the right length in the Authentication params
like even if they are 0 I had to type
aka_k="00000000000000000000000000000000" and same for aka_OP and
aka_AMF="0000". But then it actually works.
Best regards
alberto
/*
* 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
*
* Author : F. Tarek Rogers - 01 Sept 2004
* Russell Roy
* Wolfgang Beck
* Dragos Vingarzan - 02 February 2006 vingar...@gmail.com
* - split in the auth architecture
* - introduced AKAv1-MD5
* Frederique Aurouet
*/
#if defined( __FreeBSD__) || defined(__DARWIN) || defined(__SUNOS)
#include <sys/types.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "md5.h"
#include "milenage.h"
#include "screen.hpp"
#include "logger.hpp"
#include "auth.hpp"
#define MAX_HEADER_LEN 2049
#define MD5_HASH_SIZE 16
#define HASH_HEX_SIZE 2*MD5_HASH_SIZE
/* AKA */
#define KLEN 16
typedef u_char K[KLEN];
#define RANDLEN 16
typedef u_char RAND[RANDLEN];
#define AUTNLEN 16
typedef u_char AUTN[AUTNLEN];
#define AKLEN 6
typedef u_char AK[AKLEN];
#define AMFLEN 2
typedef u_char AMF[AMFLEN];
#define MACLEN 8
typedef u_char MAC[MACLEN];
#define CKLEN 16
typedef u_char CK[CKLEN];
#define IKLEN 16
typedef u_char IK[IKLEN];
#define SQNLEN 6
typedef u_char SQN[SQNLEN];
#define AUTSLEN 14
typedef char AUTS[AUTSLEN];
#define AUTS64LEN 29
typedef char AUTS64[AUTS64LEN];
#define RESLEN 8
typedef unsigned char RES[RESLEN+1];
#define RESHEXLEN 17
typedef char RESHEX[RESHEXLEN];
#define OPLEN 16
typedef u_char OP[OPLEN];
AMF amfstar="\0";
SQN sqn_he= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/* end AKA */
static int createAuthHeaderMD5(
const char* user, const char* password, int password_len,
const char* method, const char* uri, const char* msgbody,
const char* auth, const char* algo, unsigned int nonce_count,
char* result, size_t result_len);
static int createAuthHeaderAKAv1MD5(
const char* user, const char* OP, const char* AMF, const char* K,
const char* method, const char* uri, const char* msgbody,
const char* auth, const char* algo, unsigned int nonce_count,
char* result, size_t result_len);
/* This function is from RFC 2617 Section 5 */
static void hashToHex(md5_byte_t* _b_raw, unsigned char* _h)
{
unsigned short i;
unsigned char j;
unsigned char *_b = (unsigned char *) _b_raw;
for (i = 0; i < MD5_HASH_SIZE; i++) {
j = (_b[i] >> 4) & 0xf;
if (j <= 9) {
_h[i * 2] = (j + '0');
} else {
_h[i * 2] = (j + 'a' - 10);
}
j = _b[i] & 0xf;
if (j <= 9) {
_h[i * 2 + 1] = (j + '0');
} else {
_h[i * 2 + 1] = (j + 'a' - 10);
}
};
_h[HASH_HEX_SIZE] = '\0';
}
static char *stristr(const char* s1, const char* s2)
{
char *cp = (char*) s1;
char *p1, *p2, *endp;
char l, r;
endp = (char*)s1 + (strlen(s1) - strlen(s2)) ;
while (*cp && (cp <= endp)) {
p1 = cp;
p2 = (char*)s2;
while (*p1 && *p2) {
l = toupper(*p1);
r = toupper(*p2);
if (l != r) {
break;
}
p1++;
p2++;
}
if (*p2 == 0) {
return cp;
}
cp++;
}
return 0;
}
int createAuthHeader(
const char* user, const char* password, const char* method,
const char* uri, const char* msgbody, const char* auth,
const char* aka_OP, const char* aka_AMF, const char* aka_K,
unsigned int nonce_count, char* result, size_t result_len)
{
char algo[32] = "MD5";
char *start, *end;
if ((start = stristr(auth, "Digest")) == NULL) {
snprintf(result, result_len, "createAuthHeader: authentication must be
digest");
return 0;
}
if (!method) {
snprintf(result, result_len, "createAuthHeader: authentication requires
a method");
return 0;
}
if ((start = stristr(auth, "algorithm=")) != NULL) {
start = start + strlen("algorithm=");
if (*start == '"') {
start++;
}
end = start + strcspn(start, " ,\"\r\n");
strncpy(algo, start, end - start);
algo[end - start] ='\0';
}
if (strncasecmp(algo, "MD5", 3)==0) {
return createAuthHeaderMD5(
user, password, strlen(password), method, uri, msgbody,
auth, algo, nonce_count, result, result_len);
} else if (strncasecmp(algo, "AKAv1-MD5", 9)==0) {
if (!aka_K) {
snprintf(result, result_len, "createAuthHeader: AKAv1-MD5
authentication requires a key");
return 0;
}
return createAuthHeaderAKAv1MD5(
user, aka_OP, aka_AMF, aka_K, method, uri, msgbody, auth,
algo, nonce_count, result, result_len);
} else {
snprintf(result, result_len, "createAuthHeader: authentication must use
MD5 or AKAv1-MD5");
return 0;
}
}
int getAuthParameter(const char *name, const char *header, char *result, int
len)
{
char *start, *end;
start = stristr(header, name);
while (start) {
// Ensure that the preceding character is "," or whitespace - this
// stops us finding "cnonce" when we search for "nonce".
char preceding_char = start[-1];
if ((preceding_char == ',')
|| isspace(preceding_char)) {
break;
}
start = stristr(start+1, name);
}
if (!start) {
result[0] = '\0';
return 0;
}
start += strlen(name);
if (*start++ != '=') {
return getAuthParameter(name, start, result, len);
}
if (*start == '"') {
start++;
end = start;
while (*end != '"' && *end) {
end++;
}
} else {
end = start + strcspn(start, " ,\"\r\n");
}
if (end - start >= len) {
strncpy(result, start, len - 1);
result[len - 1] = '\0';
} else {
strncpy(result, start, end - start);
result[end - start] = '\0';
}
return end - start;
}
static int createAuthResponseMD5(
const char* user, const char* password, int password_len,
const char* method, const char* uri, const char* authtype,
const char* msgbody, const char* realm, const char* nonce,
const char* cnonce, const char* nc,
unsigned char* result)
{
md5_byte_t ha1[MD5_HASH_SIZE], ha2[MD5_HASH_SIZE];
md5_byte_t resp[MD5_HASH_SIZE], body[MD5_HASH_SIZE];
unsigned char body_hex[HASH_HEX_SIZE+1];
unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1];
char tmp[MAX_HEADER_LEN];
md5_state_t Md5Ctx;
// Load in A1
md5_init(&Md5Ctx);
md5_append(&Md5Ctx, (md5_byte_t *) user, strlen(user));
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) realm, strlen(realm));
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) password, password_len);
md5_finish(&Md5Ctx, ha1);
hashToHex(&ha1[0], &ha1_hex[0]);
if (auth_uri) {
snprintf(tmp, sizeof(tmp), "sip:%s", auth_uri);
} else {
strncpy(tmp, uri, sizeof(tmp) - 1);
}
// If using Auth-Int make a hash of the body - which is NULL for REG
if (stristr(authtype, "auth-int") != NULL) {
md5_init(&Md5Ctx);
md5_append(&Md5Ctx, (md5_byte_t *) msgbody, strlen(msgbody));
md5_finish(&Md5Ctx, body);
hashToHex(&body[0], &body_hex[0]);
}
// Load in A2
md5_init(&Md5Ctx);
md5_append(&Md5Ctx, (md5_byte_t *) method, strlen(method));
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) tmp, strlen(tmp));
if (stristr(authtype, "auth-int") != NULL) {
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) &body_hex, HASH_HEX_SIZE);
}
md5_finish(&Md5Ctx, ha2);
hashToHex(&ha2[0], &ha2_hex[0]);
md5_init(&Md5Ctx);
md5_append(&Md5Ctx, (md5_byte_t *) &ha1_hex, HASH_HEX_SIZE);
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) nonce, strlen(nonce));
if (cnonce[0] != '\0') {
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) nc, strlen(nc));
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) cnonce, strlen(cnonce));
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) authtype, strlen(authtype));
}
md5_append(&Md5Ctx, (md5_byte_t *) ":", 1);
md5_append(&Md5Ctx, (md5_byte_t *) &ha2_hex, HASH_HEX_SIZE);
md5_finish(&Md5Ctx, resp);
hashToHex(&resp[0], result);
return 1;
}
int createAuthHeaderMD5(
const char* user, const char* password, int password_len,
const char* method, const char* uri, const char* msgbody,
const char* auth, const char* algo, unsigned int nonce_count,
char* result, size_t result_len)
{
unsigned char resp_hex[HASH_HEX_SIZE+1];
char realm[MAX_HEADER_LEN],
sipuri[MAX_HEADER_LEN],
nonce[MAX_HEADER_LEN],
authtype[16],
cnonce[32],
nc[32],
opaque[64];
int has_opaque = 0;
int written = 0;
// Extract the Auth Type - If not present, using 'none'
cnonce[0] = '\0';
if (getAuthParameter("qop", auth, authtype, sizeof(authtype))) {
// Sloppy auth type recognition (may be "auth,auth-int")
if (stristr(authtype, "auth-int")) {
strncpy(authtype, "auth-int", sizeof(authtype) - 1);
} else if (stristr(authtype, "auth")) {
strncpy(authtype, "auth", sizeof(authtype) - 1);
}
sprintf(cnonce, "%x", rand());
sprintf(nc, "%08x", nonce_count);
}
// Extract the Opaque value - if present
if (getAuthParameter("opaque", auth, opaque, sizeof(opaque))) {
has_opaque = 1;
}
// Extract the Realm
if (!getAuthParameter("realm", auth, realm, sizeof(realm))) {
snprintf(result, result_len, "createAuthHeaderMD5: couldn't parse realm
in '%s'", auth);
return 0;
}
written += snprintf(
result + written, result_len - written,
"Digest username=\"%s\",realm=\"%s\"", user, realm);
// Construct the URI
if (auth_uri == NULL) {
snprintf(sipuri, sizeof(sipuri), "sip:%s", uri);
} else {
snprintf(sipuri, sizeof(sipuri), "sip:%s", auth_uri);
}
if (cnonce[0] != '\0') {
// No double quotes around nc and qop (RFC3261):
//
// dig-resp = username / realm / nonce / digest-uri / dresponse
// / algorithm / cnonce / opaque / message-qop
// message-qop = "qop" EQUAL ("auth" / "auth-int" / token)
// nonce-count = "nc" EQUAL 8LHEX
//
// The digest challenge does have double quotes however:
//
// digest-cln = realm / domain / nonce / opaque / stale / algorithm
// / qop-options / auth-param
// qop-options = "qop" EQUAL LDQUOT qop-value *("," qop-value) RDQUOT
written += snprintf(
result + written, result_len - written,
",cnonce=\"%s\",nc=%s,qop=%s", cnonce, nc, authtype);
}
written += snprintf(
result + written, result_len - written, ",uri=\"%s\"", sipuri);
// Extract the Nonce
if (!getAuthParameter("nonce", auth, nonce, sizeof(nonce))) {
snprintf(result, result_len, "createAuthHeader: couldn't parse nonce");
return 0;
}
createAuthResponseMD5(
user, password, strlen(password), method, sipuri, authtype,
msgbody, realm, nonce, cnonce, nc, &resp_hex[0]);
written += snprintf(
result + written, result_len - written,
",nonce=\"%s\",response=\"%s\",algorithm=%s", nonce, resp_hex, algo);
if (has_opaque) {
written += snprintf(
result + written, result_len - written, ",opaque=\"%s\"", opaque);
}
return written;
}
int verifyAuthHeader(const char *user, const char *password, const char
*method, const char *auth, const char *msgbody)
{
char algo[MAX_HEADER_LEN];
unsigned char result[HASH_HEX_SIZE + 1];
char response[HASH_HEX_SIZE + 1];
char realm[MAX_HEADER_LEN];
char nonce[MAX_HEADER_LEN];
char cnonce[MAX_HEADER_LEN];
char authtype[MAX_HEADER_LEN];
char nc[MAX_HEADER_LEN];
char uri[MAX_HEADER_LEN];
char *start;
if ((start = stristr(auth, "Digest")) == NULL) {
WARNING("verifyAuthHeader: authentication must be digest is %s", auth);
return 0;
}
getAuthParameter("algorithm", auth, algo, sizeof(algo));
if (algo[0] == '\0') {
strcpy(algo, "MD5");
}
if (strncasecmp(algo, "MD5", 3)==0) {
getAuthParameter("realm", auth, realm, sizeof(realm));
getAuthParameter("uri", auth, uri, sizeof(uri));
getAuthParameter("nonce", auth, nonce, sizeof(nonce));
getAuthParameter("cnonce", auth, cnonce, sizeof(cnonce));
getAuthParameter("nc", auth, nc, sizeof(nc));
getAuthParameter("qop", auth, authtype, sizeof(authtype));
createAuthResponseMD5(
user, password, strlen(password), method, uri, authtype,
msgbody, realm, nonce, cnonce, nc, result);
getAuthParameter("response", auth, response, sizeof(response));
TRACE_CALLDEBUG("Processing verifyauth command - user %s, password %s,
method %s, uri %s, realm %s, nonce %s, result expected %s, response from user
%s\n",
user,
password,
method,
uri,
realm,
nonce,
(char*)result,
response);
return !strcmp((char *)result, response);
} else {
WARNING("createAuthHeader: authentication must use MD5 or AKAv1-MD5,
value is '%s'", algo);
return 0;
}
}
/*"
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/
static int base64_val(char x) {
switch(x) {
case '=':
return -1;
case 'A':
return 0;
case 'B':
return 1;
case 'C':
return 2;
case 'D':
return 3;
case 'E':
return 4;
case 'F':
return 5;
case 'G':
return 6;
case 'H':
return 7;
case 'I':
return 8;
case 'J':
return 9;
case 'K':
return 10;
case 'L':
return 11;
case 'M':
return 12;
case 'N':
return 13;
case 'O':
return 14;
case 'P':
return 15;
case 'Q':
return 16;
case 'R':
return 17;
case 'S':
return 18;
case 'T':
return 19;
case 'U':
return 20;
case 'V':
return 21;
case 'W':
return 22;
case 'X':
return 23;
case 'Y':
return 24;
case 'Z':
return 25;
case 'a':
return 26;
case 'b':
return 27;
case 'c':
return 28;
case 'd':
return 29;
case 'e':
return 30;
case 'f':
return 31;
case 'g':
return 32;
case 'h':
return 33;
case 'i':
return 34;
case 'j':
return 35;
case 'k':
return 36;
case 'l':
return 37;
case 'm':
return 38;
case 'n':
return 39;
case 'o':
return 40;
case 'p':
return 41;
case 'q':
return 42;
case 'r':
return 43;
case 's':
return 44;
case 't':
return 45;
case 'u':
return 46;
case 'v':
return 47;
case 'w':
return 48;
case 'x':
return 49;
case 'y':
return 50;
case 'z':
return 51;
case '0':
return 52;
case '1':
return 53;
case '2':
return 54;
case '3':
return 55;
case '4':
return 56;
case '5':
return 57;
case '6':
return 58;
case '7':
return 59;
case '8':
return 60;
case '9':
return 61;
case '+':
return 62;
case '/':
return 63;
}
return 0;
}
static char* base64_decode_string(const char* buf, unsigned int len, int*
newlen)
{
unsigned long i;
int j, x1, x2, x3, x4;
char *out;
out = (char *)malloc( ( len * 3/4 ) + 8 );
for(i=0, j=0; i + 3 < len; i += 4) {
x1=base64_val(buf[i]);
x2=base64_val(buf[i+1]);
x3=base64_val(buf[i+2]);
x4=base64_val(buf[i+3]);
out[j++]=(x1<<2) | ((x2 & 0x30)>>4);
out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2);
out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F);
}
if (i<len) {
x1 = base64_val(buf[i]);
if (i+1<len)
x2=base64_val(buf[i+1]);
else
x2=-1;
if (i+2<len)
x3=base64_val(buf[i+2]);
else
x3=-1;
if(i+3<len)
x4=base64_val(buf[i+3]);
else x4=-1;
if (x2!=-1) {
out[j++]=(x1<<2) | ((x2 & 0x30)>>4);
if (x3==-1) {
out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2);
if (x4==-1) {
out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F);
}
}
}
}
out[j++] = 0;
*newlen=j;
return out;
}
char base64[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char hexa[17] = "0123456789abcdef";
int ahex2int(char a, char b)
{
a = (a<='9') ? a-'0' : (a & 0x7)+9;
b = (b<='9') ? b-'0' : (b & 0x7)+9;
return (a<<4) + b;
}
// hexstring has at least 2*len and result has len
static int hex2uchar(const char *hexstring,int len, u_char *result)
{
int i = 0;
int j = 0;
if (!hexstring || !result)
{
return 0;
}
if (len == 0)
{
return 0;
}
for (i=0;j<len;i++)
{
result[j] = ahex2int(hexstring[i],hexstring[i+1]);
i++;
j++;
}
return j;
}
static int createAuthHeaderAKAv1MD5(
const char* user, const char* aka_OP, const char* aka_AMF,
const char* aka_K, const char* method, const char* uri,
const char* msgbody, const char* auth, const char* algo,
unsigned int nonce_count, char* result, size_t result_len)
{
char tmp[MAX_HEADER_LEN];
char *start, *end;
int has_auts = 0;
int written = 0;
char *nonce64, *nonce;
int noncelen;
AMF amf;
OP op;
OP original_op;
RAND rnd;
AUTS auts_bin;
AUTS64 auts_hex;
MAC mac, xmac;
SQN sqn, sqnxoraka, sqn_ms;
K k;
RES res;
CK ck;
IK ik;
AK ak;
int i;
// Extract the Nonce
if ((start = stristr(auth, "nonce=")) == NULL) {
snprintf(result, result_len, "createAuthHeaderAKAv1MD5: couldn't parse
nonce");
return 0;
}
start = start + strlen("nonce=");
if (*start == '"') {
start++;
}
end = start + strcspn(start, " ,\"\r\n");
strncpy(tmp, start, end - start);
tmp[end - start] ='\0';
/* Compute the AKA RES */
nonce64 = tmp;
nonce = base64_decode_string(nonce64, end-start, &noncelen);
if (noncelen < RANDLEN + AUTNLEN) {
if (nonce)
free(nonce);
snprintf(
result, result_len,
"createAuthHeaderAKAv1MD5 : Nonce is too short %d < %d expected\n",
noncelen, RANDLEN + AUTNLEN);
return 0;
}
// nonce is a String decoded from the base64
memcpy(rnd, nonce, RANDLEN);
memcpy(sqnxoraka, nonce + RANDLEN, SQNLEN);
memcpy(mac, nonce + RANDLEN + SQNLEN + AMFLEN, MACLEN);
//here are the fundamental problems because aka_K, aka_AMF and aka_OP are
character stings and k,amf, op are byte strings
//memcpy(k, aka_K, KLEN);
hex2uchar(aka_K,KLEN,k);
//memcpy(amf,aka_AMF,AMFLEN);
hex2uchar(aka_AMF,AMFLEN,amf);
//memcpy(op, aka_OP, OPLEN);
hex2uchar(aka_OP,OPLEN,op);
hex2uchar(aka_OP,OPLEN,original_op);
/* Compute the AK, response and keys CK IK */
f2345(k, rnd, res, ck, ik, ak, op);
res[RESLEN] = '\0';
/* Compute sqn encoded in AUTN */
for (i=0; i < SQNLEN; i++)
sqn[i] = sqnxoraka[i] ^ ak[i];
/* compute XMAC */
f1(k, rnd, sqn, amf, xmac, op);
if (memcmp(mac, xmac, MACLEN) != 0) {
free(nonce);
snprintf(
result, result_len,
"createAuthHeaderAKAv1MD5 : MAC != expectedMAC \nmac has
[%02X%02X%02X%02X] and xmac was [%02X%02X%02X%02X]\n"
"Check your input parameters aka_K, aka_OP and aka_AMF which are
read as string and need to have all the characters without 0x\n"
"rnd was [%02X%02X%02X%02X]\n"
"k was [%02X%02X%02X%02X%02X%02X%02X%02X]\n"
"amf is [%02X%02X]\n op is [%02X%02X%02X%02X%02X%02X%02X%02X]\n"
"aka_OP is [%s]\n"
"original_op is [%02X%02X%02X%02X%02X%02X%02X%02X]\n"
"aka_AMF was [%s]\n"
"aka_K was [%s]\n",
mac[0],mac[1],mac[2],mac[3],xmac[0],xmac[1],xmac[2],xmac[3],rnd[0],rnd[1],rnd[2],rnd[3],
k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7], amf[0], amf[1],
op[0],op[1],op[2],op[3],op[4],op[5],op[6],op[7],aka_OP,
original_op[0],original_op[1],original_op[2],original_op[3],original_op[4],original_op[5],original_op[6],original_op[7],
aka_AMF, aka_K);
return 0;
}
/* Check SQN, compute AUTS if needed and authorization parameter */
/* the condition below is wrong.
* Should trigger synchronization when sqn_ms>>3!=sqn_he>>3 for example.
* Also, we need to store the SQN per user or put it as auth parameter. */
if (1/*sqn[5] > sqn_he[5]*/) {
sqn_he[5] = sqn[5];
has_auts = 0;
/* RES has to be used as password to compute response */
written = createAuthHeaderMD5(
user, (const char *)res, RESLEN, method, uri, msgbody, auth,
algo, nonce_count, result, result_len);
if (written == 0) {
free(nonce);
snprintf(
result, result_len,
"createAuthHeaderAKAv1MD5 : Unexpected return value from
createAuthHeaderMD5\n");
return 0;
}
} else {
sqn_ms[5] = sqn_he[5] + 1;
f5star(k, rnd, ak, op);
for(i=0; i<SQNLEN; i++)
auts_bin[i]=sqn_ms[i]^ak[i];
f1star(k, rnd, sqn_ms, amf, (unsigned char * ) (auts_bin+SQNLEN), op);
has_auts = 1;
/* When re-synchronisation occurs an empty password has to be used */
/* to compute MD5 response (Cf. rfc 3310 section 3.2) */
written = createAuthHeaderMD5(
user, "", 0, method, uri, msgbody, auth, algo, nonce_count,
result, result_len);
if (written == 0) {
free(nonce);
snprintf(
result, result_len,
"createAuthHeaderAKAv1MD5 : Unexpected return value from
createAuthHeaderMD5\n");
return 0;
}
}
if (has_auts) {
/* Format data for output in the SIP message */
for (i = 0; i < AUTSLEN; i++) {
auts_hex[2*i] = hexa[(auts_bin[i]&0xF0)>>4];
auts_hex[2*i+1] = hexa[auts_bin[i]&0x0F];
}
auts_hex[AUTS64LEN-1] = 0;
written += snprintf(
result + written, result_len - written, ",auts=\"%s\"", auts_hex);
}
free(nonce);
return written;
}
#ifdef GTEST
#include "gtest/gtest.h"
TEST(DigestAuth, nonce) {
char nonce[40];
getAuthParameter("nonce", " Authorization: Digest
cnonce=\"c7e1249f\",nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", nonce,
sizeof(nonce));
EXPECT_STREQ("a6ca2bf13de1433183f7c48781bd9304", nonce);
getAuthParameter("nonce", " Authorization: Digest
nonce=\"a6ca2bf13de1433183f7c48781bd9304\", cnonce=\"c7e1249f\"", nonce,
sizeof(nonce));
EXPECT_STREQ("a6ca2bf13de1433183f7c48781bd9304", nonce);
}
TEST(DigestAuth, cnonce) {
char cnonce[10];
getAuthParameter("cnonce", " Authorization: Digest
cnonce=\"c7e1249f\",nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", cnonce,
sizeof(cnonce));
EXPECT_STREQ("c7e1249f", cnonce);
getAuthParameter("cnonce", " Authorization: Digest
nonce=\"a6ca2bf13de1433183f7c48781bd9304\", cnonce=\"c7e1249f\"", cnonce,
sizeof(cnonce));
EXPECT_STREQ("c7e1249f", cnonce);
}
TEST(DigestAuth, MissingParameter) {
char cnonce[10];
getAuthParameter("cnonce", " Authorization: Digest
nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", cnonce, sizeof(cnonce));
EXPECT_EQ('\0', cnonce[0]);
}
TEST(DigestAuth, BasicVerification) {
char* header = strdup(("Digest \r\n"
" realm=\"testre...@host.com\",\r\n"
" nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"\r\n,"
" opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""));
char result[255];
createAuthHeader("testuser", "secret", "REGISTER", "sip:example.com",
"hello world", header, NULL, NULL, NULL, 1, result, 255);
EXPECT_STREQ("Digest
username=\"testuser\",realm=\"testre...@host.com\",uri=\"sip:sip:example.com\",nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",response=\"db94e01e92f2b09a52a234eeca8b90f7\",algorithm=MD5,opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"",
result);
EXPECT_EQ(1, verifyAuthHeader("testuser", "secret", "REGISTER", result,
"hello world"));
free(header);
}
TEST(DigestAuth, qop) {
char result[1024];
char* header = strdup(("Digest \r\n"
"\trealm=\"testre...@host.com\",\r\n"
"\tqop=\"auth,auth-int\",\r\n"
"\tnonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"\r\n,"
"\topaque=\"5ccc069c403ebaf9f0171e9517f40e41\""));
createAuthHeader("testuser",
"secret",
"REGISTER",
"sip:example.com",
"hello world",
header,
NULL,
NULL,
NULL,
1,
result,
1024);
EXPECT_EQ(1, !!strstr(result, ",qop=auth-int,")); // no double quotes
around qop-value
EXPECT_EQ(1, verifyAuthHeader("testuser", "secret", "REGISTER", result,
"hello world"));
free(header);
}
#endif //GTEST
_______________________________________________
Sipp-users mailing list
Sipp-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sipp-users