diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn index 9402f0d27..dab940021 100644 --- a/asterisk/Asterisk_Tests.ttcn +++ b/asterisk/Asterisk_Tests.ttcn @@ -11,6 +11,7 @@ module Asterisk_Tests { * SPDX-License-Identifier: GPL-2.0-or-later */ +import from TCCOpenSecurity_Functions all; import from General_Types all; import from Osmocom_Types all; import from Native_Functions all; @@ -44,6 +45,7 @@ type component ConnHdlr extends SIP_ConnHdlr { type record ConnHdlrPars { float t_guard, charstring user, + charstring password, SipUrl registrar_sip_url, SipAddr registrar_sip_record, CallidString registrar_sip_call_id, @@ -55,9 +57,11 @@ type record ConnHdlrPars { } template (value) ConnHdlrPars t_Pars(charstring user, - charstring displayname := "\"Anonymous\"") := { + charstring displayname := "\"Anonymous\"", + charstring password := "secret") := { t_guard := 30.0, user := user, + password := password, registrar_sip_url := valueof(ts_SipUrlHost(mp_remote_sip_host)), registrar_sip_record := ts_SipAddr(ts_HostPort(mp_remote_sip_host), ts_UserInfo(user), @@ -188,38 +192,87 @@ private function f_tr_Via_response(Via via_req) return template (present) Via { via_resp_params); } +private function f_tr_To_response(SipAddr to_req) return template (present) SipAddr { + return tr_SipAddr_from_val(to_req); +} + function f_SIP_register() runs on ConnHdlr return PDU_SIP_Response { var template (present) PDU_SIP_Response exp; + var Authorization authorization; + var Via via := g_pars.registrar_via; + var SipAddr from_sipaddr := g_pars.registrar_sip_record; + var charstring branch_value; + branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record), + f_sip_SipAddr_to_str(g_pars.registrar_sip_record), + g_pars.registrar_sip_call_id, + g_pars.registrar_sip_seq_nr); + + via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value); + from_sipaddr.params := f_sip_param_set(from_sipaddr.params, "tag", f_sip_rand_tag()); SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_url, g_pars.registrar_sip_call_id, + from_sipaddr, g_pars.registrar_sip_record, - g_pars.registrar_sip_record, - g_pars.registrar_via, + via, g_pars.registrar_sip_seq_nr, g_pars.local_contact, ts_Expires("7200"))); exp := tr_SIP_Response_REGISTER_Unauthorized( g_pars.registrar_sip_call_id, - g_pars.registrar_sip_record, - g_pars.registrar_sip_record, - f_tr_Via_response(g_pars.registrar_via), + from_sipaddr, + f_tr_To_response(g_pars.registrar_sip_record), + f_tr_Via_response(via), *, + tr_WwwAuthenticate({tr_Challenge_digestCln(?)}), g_pars.registrar_sip_seq_nr); as_SIP_expect_resp(exp); - /* Do the registering after calculating the md5 hash, etc. */ + /* Digest Auth: RFC 2617 */ + authorization := f_sip_digest_gen_Authorization(g_rx_sip_resp.msgHeader.wwwAuthenticate, + g_pars.user, g_pars.password, + "REGISTER", "sip:" & mp_remote_sip_host) + + /* New transaction: */ + g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1; + branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record), + f_sip_SipAddr_to_str(g_pars.registrar_sip_record), + g_pars.registrar_sip_call_id, + g_pars.registrar_sip_seq_nr); + via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value); + + SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_url, + g_pars.registrar_sip_call_id, + from_sipaddr, + g_pars.registrar_sip_record, + via, + g_pars.registrar_sip_seq_nr, + g_pars.local_contact, + ts_Expires("7200"), + authorization := authorization)); + + /* Wait for OK answer */ + exp := tr_SIP_Response( + g_pars.registrar_sip_call_id, + from_sipaddr, + f_tr_To_response(g_pars.registrar_sip_record), + f_tr_Via_response(via), + *, + "REGISTER", 200, + g_pars.registrar_sip_seq_nr, "OK"); + as_SIP_expect_resp(exp); + + /* Prepare for next use: */ + g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1; return g_rx_sip_resp; } -/* Successful MO Call, which is subsequently released by SIP side */ +/* Test SIP registration of local clients */ private function f_TC_internal_registration(charstring id) runs on ConnHdlr { f_SIP_register(); - /* now call is fully established */ - f_sleep(2.0); // f_SIP_deregister(); setverdict(pass); } @@ -233,6 +286,11 @@ testcase TC_internal_registration() runs on test_CT { vc_conn.done; } +testcase TC_selftest() runs on test_CT { + f_sip_digest_selftest(); + setverdict(pass); +} + control { execute( TC_internal_registration() ); } diff --git a/asterisk/gen_links.sh b/asterisk/gen_links.sh index 1fd6ecc19..7394b6451 100755 --- a/asterisk/gen_links.sh +++ b/asterisk/gen_links.sh @@ -5,7 +5,8 @@ BASEDIR=../deps . ../gen_links.sh.inc DIR=$BASEDIR/titan.Libraries.TCCUsefulFunctions/src -FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h" +FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h " +FILES+="TCCOpenSecurity_Functions.ttcn TCCOpenSecurity.cc TCCOpenSecurity_Functions.hh " gen_links $DIR $FILES DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src diff --git a/asterisk/regen_makefile.sh b/asterisk/regen_makefile.sh index 3995b3d93..3cd0a66aa 100755 --- a/asterisk/regen_makefile.sh +++ b/asterisk/regen_makefile.sh @@ -14,6 +14,7 @@ FILES=" SIPmsg_PT.cc TCCConversion.cc TCCInterface.cc + TCCOpenSecurity.cc " ../regen-makefile.sh -e $NAME $FILES diff --git a/library/SIP_Templates.ttcn b/library/SIP_Templates.ttcn index fc8e23f66..7cb9d68d9 100644 --- a/library/SIP_Templates.ttcn +++ b/library/SIP_Templates.ttcn @@ -1,7 +1,11 @@ module SIP_Templates { import from SIPmsg_Types all; +import from TCCConversion_Functions all; +import from TCCOpenSecurity_Functions all; +import from Native_Functions all; import from Osmocom_Types all; +import from Misc_Helpers all; /* wrapper type to encapsulate the Addr_Union + parameter list used in From, To. ... */ type record SipAddr { @@ -11,6 +15,26 @@ type record SipAddr { const charstring c_SIP_VERSION := "SIP/2.0"; +template (value) GenericParam ts_Param(template (value) charstring id, + template (omit) charstring paramValue := omit) := { + id := id, + paramValue := paramValue +} +template (present) GenericParam tr_Param(template (present) charstring id := ?, + template charstring paramValue := *) := { + id := id, + paramValue := paramValue +} +function f_ts_Param_omit(template (value) charstring id, + template (omit) charstring paramValue := omit) + return template (omit) GenericParam +{ + if (istemplatekind(paramValue, "omit")) { + return omit; + } + return ts_Param(id, paramValue); +} + template (value) SipUrl ts_SipUrl(template (value) HostPort host_port, template (omit) UserInfo user_info := omit) := { scheme := "sip", @@ -31,6 +55,49 @@ template (present) SipUrl tr_SipUrl(template (present) HostPort host_port := ?, template (value) SipUrl ts_SipUrlHost(template (value) charstring host) := ts_SipUrl(ts_HostPort(host)); +template (value) Credentials ts_Credentials_DigestResponse(template (value) CommaParam_List digestResponse) := { + digestResponse := digestResponse +} + +template (value) Credentials ts_Credentials_DigestResponseMD5( + template (value) charstring username, + template (value) charstring realm, + template (value) charstring nonce, + template (value) charstring uri, + template (value) charstring response, + template (value) charstring opaque, + template (value) charstring algorithm := "MD5", + template (value) charstring qop := "auth", + template (omit) charstring cnonce := omit, + template (omit) charstring nc := omit + ) := { + digestResponse := { + // Already added by digestResponse automatically: + //ts_Param("Digest", omit), + ts_Param("username", f_sip_str_quote(username)), + ts_Param("realm", f_sip_str_quote(realm)), + ts_Param("nonce", f_sip_str_quote(nonce)), + ts_Param("uri", f_sip_str_quote(uri)), + ts_Param("response", f_sip_str_quote(response)), + ts_Param("opaque", f_sip_str_quote(opaque)), + ts_Param("algorithm", algorithm), + ts_Param("qop", qop), + // FIXME: If "omit" is passed, these below end up in; + // "Dynamic test case error: Performing a valueof or send operation on a non-specific template of type @SIPmsg_Types.GenericParam" + f_ts_Param_omit("cnonce", f_sip_str_quote(cnonce)), + f_ts_Param_omit("nc", nc) + } +} + +template (value) Credentials ts_Credentials_OtherAuth(template (value) OtherAuth otherResponse) := { + otherResponse := otherResponse +} + +template (value) Authorization ts_Authorization(template (value) Credentials body) := { + fieldName := AUTHORIZATION_E, + body := body +} + // [20.10] template (present) NameAddr tr_NameAddr(template (present) SipUrl addrSpec := ?, template charstring displayName := *) := { @@ -262,7 +329,53 @@ template (present) Via tr_Via_from(template (present) HostPort host_port := ?, viaParams := viaParams } } - } +} + +template (present) OtherAuth +tr_OtherAuth(template (present) charstring authScheme := ?, + template (present) CommaParam_List authParams := ?) := { + authScheme := authScheme, + authParams := authParams +} + +template (value) OtherAuth +ts_OtherAuth(template (value) charstring authScheme, + template (value) CommaParam_List authParams) := { + authScheme := authScheme, + authParams := authParams +} + +template (present) Challenge +tr_Challenge_digestCln(template (present) CommaParam_List digestCln := ?) := { + digestCln := digestCln +} + +template (value) Challenge +ts_Challenge_digestCln(template (value) CommaParam_List digestCln) := { + digestCln := digestCln +} + +template (present) Challenge +tr_Challenge_otherChallenge(template (present) OtherAuth otherChallenge := ?) := { + otherChallenge := otherChallenge +} + +template (value) Challenge +ts_Challenge_otherChallenge(template (value) OtherAuth otherChallenge) := { + otherChallenge := otherChallenge +} + +template (present) WwwAuthenticate +tr_WwwAuthenticate(template (present) Challenge_list challenge := ?) := { + fieldName := WWW_AUTHENTICATE_E, + challenge := challenge +} + +template (value) WwwAuthenticate +ts_WwwAuthenticate(template (value) Challenge_list challenge) := { + fieldName := WWW_AUTHENTICATE_E, + challenge := challenge +} template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty; template (value) MessageHeader @@ -274,6 +387,7 @@ ts_SIP_msgh_std(template (value) CallidString call_id, template (value) integer seq_nr, template (value) Via via, template (omit) ContentType content_type := omit, + template (omit)Authorization authorization := omit, template (value) Method_List allow_methods := c_SIP_defaultMethods, template (omit) Expires expires := omit ) modifies ts_SIP_msgHeader_empty := { @@ -281,6 +395,7 @@ ts_SIP_msgh_std(template (value) CallidString call_id, fieldName := ALLOW_E, methods := allow_methods }, + authorization := authorization, callId := { fieldName := CALL_ID_E, callid := call_id @@ -337,7 +452,8 @@ tr_SIP_msgh_std(template CallidString call_id, template ContentType content_type := *, template integer seq_nr := ?, template Method_List allow_methods := *, - template Expires expires := * + template Expires expires := *, + template WwwAuthenticate wwwAuthenticate := * ) modifies t_SIP_msgHeader_any := { allow := tr_AllowMethods(allow_methods), callId := { @@ -363,7 +479,8 @@ tr_SIP_msgh_std(template CallidString call_id, toParams := to_addr.params }, userAgent := *, - via := via + via := via, + wwwAuthenticate := wwwAuthenticate } @@ -376,11 +493,13 @@ ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port, integer seq_nr, template (omit) Contact contact, template (omit) Expires expires, + template (omit) Authorization authorization := omit, template (omit) charstring body := omit) := { requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact, "REGISTER", seq_nr, via, f_ContentTypeOrOmit(ts_CT_SDP, body), + authorization := authorization, expires := expires), messageBody := body, payload := omit @@ -511,15 +630,16 @@ template (present) PDU_SIP_Response tr_SIP_Response(template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, + template (present) Via via := tr_Via_from(?), template Contact contact, template charstring method, template integer status_code, template integer seq_nr := ?, template charstring reason := ?, - template charstring body := ?) := { + template charstring body := *) := { statusLine := tr_SIP_StatusLine(status_code, reason), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact, - tr_Via_from(tr_HostPort(from_addr.addr.nameAddr.addrSpec.hostPort)), + via, method, *, seq_nr), messageBody := body, payload := omit @@ -533,6 +653,7 @@ tr_SIP_Response_REGISTER_Unauthorized( template SipAddr to_addr, template (present) Via via := tr_Via_from(?), template Contact contact := *, + template (present) WwwAuthenticate wwwAuthenticate := ?, template integer seq_nr := ?, template charstring method := "REGISTER", template integer status_code := 401, @@ -541,11 +662,333 @@ tr_SIP_Response_REGISTER_Unauthorized( statusLine := tr_SIP_StatusLine(status_code, reason), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact, via, - method, *, seq_nr), + method, *, seq_nr, + wwwAuthenticate := wwwAuthenticate), messageBody := body, payload := omit } +function f_sip_param_find(GenericParam_List li, + template (present) charstring id := ?) +return template (omit) GenericParam { + var integer i; + + for (i := 0; i < lengthof(li); i := i + 1) { + if (not ispresent(li[i])) { + continue; + } + if (match(li[i].id, id)) { + return li[i]; + } + } + return omit; +} + +function f_sip_param_find_or_fail(GenericParam_List li, + template (present) charstring id := ?) +return GenericParam { + var template (omit) GenericParam parameter; + parameter := f_sip_param_find(li, id); + if (istemplatekind(parameter, "omit")) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Param ", id, " not found in ", li)); + } + return valueof(parameter); +} + +function f_sip_param_get_value(GenericParam_List li, + template (present) charstring id := ?) +return template (omit) charstring { + var template (omit) GenericParam parameter; + parameter := f_sip_param_find(li, id); + if (istemplatekind(parameter, "omit")) { + return omit; + } + return parameter.paramValue; +} + +function f_sip_param_get_value_or_fail(GenericParam_List li, + template (present) charstring id := ?) +return template (omit) charstring { + var GenericParam parameter; + parameter := f_sip_param_find_or_fail(li, id); + return parameter.paramValue; +} + +function f_sip_param_get_value_present_or_fail(GenericParam_List li, + template (present) charstring id := ?) +return charstring { + var GenericParam parameter; + parameter := f_sip_param_find_or_fail(li, id); + if (not ispresent(parameter.paramValue)) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Param ", id, " value not present in ", li)); + } + return parameter.paramValue; +} + +function f_sip_param_match_value(GenericParam_List li, + template (present) charstring id := ?, + template charstring exp_paramValue := *) +return boolean { + var template (omit) charstring val; + val := f_sip_param_get_value_or_fail(li, id); + if (istemplatekind(val, "omit")) { + return istemplatekind(val, "omit") or istemplatekind(val, "*"); + } + return match(valueof(val), exp_paramValue); +} + +function f_sip_param_match_value_or_fail(GenericParam_List li, + template (present) charstring id := ?, + template charstring exp_paramValue := *) +{ + var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id); + if (istemplatekind(val, "omit")) { + if (istemplatekind(val, "omit") or istemplatekind(val, "*")) { + return; + } else { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Param ", id, " match failed: val ", val, + " vs exp ", exp_paramValue)); + } + } + if (not match(valueof(val), exp_paramValue)) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Param ", id, " match failed: val ", val, + " vs exp ", exp_paramValue)); + } +} + +function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id) +return GenericParam_List { + var integer i; + var GenericParam_List li; + var GenericParam_List new_li := {}; + + if (istemplatekind(li_tpl, "omit")) { + return {}; + } + + li := valueof(li_tpl); + for (i := 0; i < lengthof(li); i := i + 1) { + if (not ispresent(li[i]) or + not match(li[i].id, id)) { + new_li := new_li & {li[i]}; + } + } + return new_li; +} + +function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val) +return GenericParam_List { + var integer i; + var GenericParam_List li; + var GenericParam_List new_li := {}; + var boolean found := false; + + if (istemplatekind(li_tpl, "omit")) { + return { valueof(ts_Param(id, val)) }; + } + + li := valueof(li_tpl); + for (i := 0; i < lengthof(li); i := i + 1) { + if (not ispresent(li[i]) or + not match(li[i].id, id)) { + new_li := new_li & {li[i]}; + continue; + } + new_li := new_li & { valueof(ts_Param(li[i].id, val)) }; + found := true; + } + + if (not found) { + new_li := new_li & { valueof(ts_Param(id, val)) }; + } + return new_li; +} + +/* Make sure string is quoted. */ +function f_sip_str_quote(template (value) charstring val) return charstring { + var charstring str := valueof(val); + if (lengthof(str) == 0) { + return ""; + } + + if (str[0] != "\"") { + return "\"" & str & "\""; + } + return str; +} + +/* Make sure string is unquoted. + * Similar to unq() in RFC 2617 */ +function f_sip_str_unquote(template (value) charstring val) return charstring { + var charstring str := valueof(val); + var integer len := lengthof(str); + + if (len <= 1) { + return str; + } + + if (str[0] == "\"" and str[len - 1] == "\"") { + return substr(str, 1, len - 2); + } + return str; +} + +/* RFC 2617 3.2.2.2 A1 */ +function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring { + + /* RFC 2617 3.2.2.2 A1 */ + var charstring A1 := f_sip_str_unquote(user) & ":" & + f_sip_str_unquote(realm) & ":" & + password; + var charstring digestA1 := f_str_tolower(f_calculateMD5(A1)); + log("A1: md5('", A1, "') = ", digestA1); + return digestA1; +} + +/* RFC 2617 3.2.2.2 A2 */ +function f_sip_digest_A2(charstring method, charstring uri) return charstring { + + var charstring A2 := method & ":" & uri + var charstring digestA2 := f_str_tolower(f_calculateMD5(A2)); + log("A2: md5('", A2, "') = ", digestA2); + return digestA2; +} + +/* RFC 2617 3.2.2.1 Request-Digest */ +function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce, + charstring nc, charstring cnonce, + charstring qop, charstring digestA2) return charstring { + var charstring digest_data := f_sip_str_unquote(nonce) & ":" & + nc & ":" & + cnonce & ":" & + f_sip_str_unquote(qop) & ":" & + digestA2; + var charstring req_digest := f_sip_digest_KD(digestA1, digest_data); + log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest); + return req_digest; +} + +/* RFC 2617 3.2.1 The WWW-Authenticate Response Header + * KD(secret, data) = H(concat(secret, ":", data)) + */ +function f_sip_digest_KD(charstring secret, charstring data) return charstring { + return f_str_tolower(f_calculateMD5(secret & ":" & data)); +} + +/* Digest Auth: RFC 2617 */ +function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate, + charstring user, charstring password, + charstring method, charstring uri, + charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization { + var CommaParam_List digestCln; + var template (value) Authorization authorization; + var template (value) Credentials cred; + var template (omit) GenericParam rx_param; + + digestCln := www_authenticate.challenge[0].digestCln; + + var charstring algorithm; + rx_param := f_sip_param_find(digestCln, "algorithm"); + if (istemplatekind(rx_param, "omit")) { + /* Assume MD5 if not set */ + algorithm := "MD5" + } else { + algorithm := valueof(rx_param.paramValue); + if (f_strstr(algorithm, "MD5") == -1) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("Unexpected algorithm: ", algorithm)); + } + } + + var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm"); + var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce"); + var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque"); + var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop"); + + if (f_strstr(qop, "auth") == -1) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop)); + } + var charstring selected_qop := "auth"; + + /* RFC 2617 3.2.2.2 A1 */ + var charstring digestA1 := f_sip_digest_A1(user, realm, password); + /* RFC 2617 3.2.2.3 A2 */ + var charstring digestA2 := f_sip_digest_A2(method, uri); + + /* RFC 2617 3.2.2.1 Request-Digest */ + var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8))); + var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce, + nc, cnonce, + selected_qop, digestA2); + + cred := ts_Credentials_DigestResponseMD5(user, realm, nonce, + uri, req_digest, + opaque, algorithm, selected_qop, cnonce, nc); + + authorization := ts_Authorization(cred); + return valueof(authorization); +} + +/* RFC 2617 3.5 Example */ +function f_sip_digest_selftest() { +/* +The following example assumes that an access-protected document is +being requested from the server via a GET request. The URI of the +document is "http://www.nowhere.org/dir/index.html". Both client and +server know that the username for this document is "Mufasa", and the +password is "Circle Of Life" (with one space between each of the +three words). + +HTTP/1.1 401 Unauthorized +WWW-Authenticate: Digest + realm="testrealm@host.com", + qop="auth,auth-int", + nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", + opaque="5ccc069c403ebaf9f0171e9517f40e41" + +Authorization: Digest username="Mufasa", + realm="testrealm@host.com", + nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", + uri="/dir/index.html", + qop=auth, + nc=00000001, + cnonce="0a4f113b", + response="6629fae49393a05397450978507c4ef1", + opaque="5ccc069c403ebaf9f0171e9517f40e41" +*/ + var template (value) CommaParam_List digestCln := { + ts_Param("realm", f_sip_str_quote("testrealm@host.com")), + ts_Param("qop", f_sip_str_quote("auth,auth-int")), + ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")), + ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41")) + }; + var template (value) WwwAuthenticate www_authenticate := + ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } ) + + var Authorization authorization := + f_sip_digest_gen_Authorization(valueof(www_authenticate), + "Mufasa", + "Circle Of Life", + "GET", + "/dir/index.html", + cnonce := "0a4f113b", + nc_int := 1); + + var CommaParam_List digestResp := authorization.body.digestResponse; + f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com")); + f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")); + f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html")); + f_sip_param_match_value_or_fail(digestResp, "qop", "auth"); + f_sip_param_match_value_or_fail(digestResp, "nc", "00000001"); + f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b")); + f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1")); + f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41")); +} + /* RFC 3261 8.1.1.5: * "The sequence number value MUST be expressible as a 32-bit unsigned integer * and MUST be less than 2**31." @@ -555,4 +998,68 @@ function f_sip_rand_seq_nr() return integer { return f_rnd_int(2147483648) } +/* Tags shall have at least 32 bit of randomness */ +function f_sip_rand_tag() return charstring { + var integer rnd_int := f_rnd_int(4294967296); + return hex2str(int2hex(rnd_int, 8)); +} + +/* Generate a "branch" tag value. + * RFC 3261 p.105 section 8: + * "A common way to create this value is to compute a + * cryptographic hash of the To tag, From tag, Call-ID header + * field, the Request-URI of the request received (before + * translation), the topmost Via header, and the sequence number + * from the CSeq header field, in addition to any Proxy-Require + * and Proxy-Authorization header fields that may be present. The + * algorithm used to compute the hash is implementation-dependent, + * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable + * choice." + * See also Section 8.1.1.7: + * "The branch ID inserted by an element compliant with this + * specification MUST always begin with the characters "z9hG4bK"." + */ +const charstring sip_magic_cookie := "z9hG4bK"; +function f_sip_gen_branch(charstring tag_to, + charstring tag_from, + charstring tag_call_id, + integer cseq) return charstring { + var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq); + var charstring hash := f_calculateMD5(str); + var charstring branch := sip_magic_cookie & hash; + return branch; +} + +function f_sip_HostPort_to_str(HostPort host_port) return charstring { + var charstring str := ""; + if (ispresent(host_port.host)) { + str := host_port.host; + } + if (ispresent(host_port.portField)) { + str := str & ":" & int2str(host_port.portField); + } + return str; +} + +function f_sip_SipUrl_to_str(SipUrl uri) return charstring { + var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort); + return str; +} + +function f_sip_NameAddr_to_str(NameAddr naddr) return charstring { + if (ispresent(naddr.displayName)) { + return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">"; + } else { + return f_sip_SipUrl_to_str(naddr.addrSpec); + } +} + +function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring { + if (ischosen(sip_addr.addr.nameAddr)) { + return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr); + } else { + return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion); + } +} + } diff --git a/sip/SIP_Tests.ttcn b/sip/SIP_Tests.ttcn index aee1a823f..4c6be1cf2 100644 --- a/sip/SIP_Tests.ttcn +++ b/sip/SIP_Tests.ttcn @@ -274,7 +274,9 @@ function f_establish_mt(inout CallPars cp) runs on ConnHdlr { } /* OSC -> SIP */ - as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *, + as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, + tr_Via_from(tr_HostPort(sip_addr_ext.addr.nameAddr.addrSpec.hostPort)), + *, "INVITE", 100, ?, "Trying", *)); alt { @@ -328,7 +330,9 @@ function f_establish_mt(inout CallPars cp) runs on ConnHdlr { SIP.clear; /* 180 Ringing should not contain any SDP. */ - as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *, + as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, + tr_Via_from(tr_HostPort(sip_addr_ext.addr.nameAddr.addrSpec.hostPort)), + *, "INVITE", 180, ?, "Ringing", omit)); /* MSC -> OSC: After MT user has picked up and sent CC CONNECT */ @@ -342,7 +346,9 @@ function f_establish_mt(inout CallPars cp) runs on ConnHdlr { } /* OSC -> SIP: OSC confirms call establishment to SIP side */ - as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, contact := ?, + as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, + tr_Via_from(tr_HostPort(sip_addr_ext.addr.nameAddr.addrSpec.hostPort)), + contact := ?, method := "INVITE", status_code := 200, seq_nr := ?, reason := "OK", body := expect_sdp_to_sip)); @@ -506,7 +512,9 @@ function f_release_sip(inout CallPars cp) runs on ConnHdlr { /* MSC -> OSC: Indicate GSM side release */ MNCC.send(ts_MNCC_REL_ind(cp.mncc_call_id, ts_MNCC_cause(0))); /* OSC -> SIP: Confirmation to SIP side */ - as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *, + as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, + tr_Via_from(tr_HostPort(sip_addr_ext.addr.nameAddr.addrSpec.hostPort)), + *, "BYE", 200, cp.comp.sip_seq_nr, "OK", omit)); } diff --git a/sip/gen_links.sh b/sip/gen_links.sh index 81c157869..d12e23c2d 100755 --- a/sip/gen_links.sh +++ b/sip/gen_links.sh @@ -9,7 +9,8 @@ FILES="UD_PT.cc UD_PT.hh UD_PortType.ttcn UD_Types.ttcn" gen_links $DIR $FILES DIR=$BASEDIR/titan.Libraries.TCCUsefulFunctions/src -FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h" +FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h " +FILES+="TCCOpenSecurity_Functions.ttcn TCCOpenSecurity.cc TCCOpenSecurity_Functions.hh " gen_links $DIR $FILES DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src diff --git a/sip/regen_makefile.sh b/sip/regen_makefile.sh index c7a645da4..ecec62b34 100755 --- a/sip/regen_makefile.sh +++ b/sip/regen_makefile.sh @@ -17,6 +17,7 @@ FILES=" SIPmsg_PT.cc TCCConversion.cc TCCInterface.cc + TCCOpenSecurity.cc TELNETasp_PT.cc UD_PT.cc "