pespin has submitted this change. ( 
https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/36532?usp=email )

Change subject: asterisk: Implement and test SIP Digest Authorization
......................................................................

asterisk: Implement and test SIP Digest Authorization

Related: SYS#6782
Change-Id: Ib469f1906927a3f246876040086ff115fbf4c032
---
M asterisk/Asterisk_Tests.ttcn
M asterisk/gen_links.sh
M asterisk/regen_makefile.sh
M library/SIP_Templates.ttcn
M sip/SIP_Tests.ttcn
M sip/gen_links.sh
M sip/regen_makefile.sh
7 files changed, 609 insertions(+), 22 deletions(-)

Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, approved




diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn
index 9402f0d..dab9400 100644
--- a/asterisk/Asterisk_Tests.ttcn
+++ b/asterisk/Asterisk_Tests.ttcn
@@ -11,6 +11,7 @@
  * 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 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 @@
 }

 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 @@
                            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 @@
        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 1fd6ecc..7394b64 100755
--- a/asterisk/gen_links.sh
+++ b/asterisk/gen_links.sh
@@ -5,7 +5,8 @@
 . ../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 3995b3d..3cd0a66 100755
--- a/asterisk/regen_makefile.sh
+++ b/asterisk/regen_makefile.sh
@@ -14,6 +14,7 @@
        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 fc8e23f..7cb9d68 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 @@

 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 (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 @@
                        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 @@
                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 @@
                fieldName := ALLOW_E,
                methods := allow_methods
        },
+       authorization := authorization,
        callId := {
                fieldName := CALL_ID_E,
                callid := call_id
@@ -337,7 +452,8 @@
                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 @@
                toParams := to_addr.params
        },
        userAgent := *,
-       via := via
+       via := via,
+       wwwAuthenticate := wwwAuthenticate
 }


@@ -376,11 +493,13 @@
                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 @@
 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 @@
        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 @@
        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="testre...@host.com",
+       qop="auth,auth-int",
+       nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+       opaque="5ccc069c403ebaf9f0171e9517f40e41"
+
+Authorization: Digest username="Mufasa",
+       realm="testre...@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("testre...@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("testre...@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 @@
        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 aee1a82..4c6be1c 100644
--- a/sip/SIP_Tests.ttcn
+++ b/sip/SIP_Tests.ttcn
@@ -274,7 +274,9 @@
        }

        /* 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 @@
        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 @@
        }

        /* 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 @@
        /* 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 81c1578..d12e23c 100755
--- a/sip/gen_links.sh
+++ b/sip/gen_links.sh
@@ -9,7 +9,8 @@
 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 c7a645d..ecec62b 100755
--- a/sip/regen_makefile.sh
+++ b/sip/regen_makefile.sh
@@ -17,6 +17,7 @@
        SIPmsg_PT.cc
        TCCConversion.cc
        TCCInterface.cc
+       TCCOpenSecurity.cc
        TELNETasp_PT.cc
        UD_PT.cc
 "

--
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/36532?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: Ib469f1906927a3f246876040086ff115fbf4c032
Gerrit-Change-Number: 36532
Gerrit-PatchSet: 3
Gerrit-Owner: pespin <pes...@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanits...@sysmocom.de>
Gerrit-Reviewer: jolly <andr...@eversberg.eu>
Gerrit-Reviewer: laforge <lafo...@osmocom.org>
Gerrit-Reviewer: osmith <osm...@sysmocom.de>
Gerrit-Reviewer: pespin <pes...@sysmocom.de>
Gerrit-MessageType: merged

Reply via email to