extends Node # Stream XMPP # Author : AleaJactaEst # # https://xmpp.org/extensions/xep-0178.html # https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml # https://datatracker.ietf.org/doc/html/rfc6120#section-6.5 # Boucle # - non connecte # - connecte en claire # - connecte en TLS # - lance l'authentification # - step authentification # - identifie signal send_msg_debug(message:String) signal send_msg_error(message:String) signal chat_connected() signal new_stanza(stanza) enum StreamState { END, # Stream is over. Starting with this. START, # Stream hass started. TLS, # Connection has been made and the first header is send. Let's get ssl! AUTHENTICATE, # We have negotiated whether to use TLS, let's authenticate. STANZA # We have authenticated, account is now allowed to send stanzas } var stream_status = StreamState.END enum XMPPState { NONE, # Nothing is configured TRY_CONNECT_SERVER, # Try to connect to server CONNECTED_SERVER, # Confirmed we are connected to server INTIALIZE, # Initialize first connection with server INTIALIZED, # when initialize is finished FEATURE_SERVER, # We received Feature (TLS ?) START_TLS, # We start TLS WAIT_TLS, # Wait TLS Done MISSING_TLS, # Missing feature TLS -> wait 30min and retry STARTED_TLS, # TLS connection is done SELECT_MECHANISM_AUTHENTICATE, AUTHENTICATE_STEP_PLAIN, # Select mechanism authenticate PLAIN AUTHENTICATE_STEP_DIGEST_MD5, # AUTHENTICATE_STEP_1, # Launch authenticate # AUTHENTICATE_STEP_2, # Launch authenticate # AUTHENTICATE_STEP_3, # Launch authenticate # AUTHENTICATE_CHECK_RESULT, # Last time check result AUTHENTICATE_FAILURE, AUTHENTICATE_SUCCESS, AUTHENTICATED, # We finished authenticate NOT_AUTHORIZED, # Not Authorize ACCOUNT_DISABLED, # Account Disable SENDED_START_STREAM, } enum ResultAuthenticate { NONE, # Authenticate not started DONE, # Authenticate finished and done ABORT, # Authenticate finished with error (try other method) NEXT, # Authenticate need execute other step } enum SASLReturn { SUCCESS, ABORTED, ACCOUNT_DISABLED, CREDENTIALS_EXPIRED, ENCRYPTION_REQUIRED, INCORRECT_ENCODING, INVALID_AUTHZID, INVALID_MECHANISM, MALFORMED_REQUEST, MECHANISM_TOO_WEAK, NOT_AUTHORIZED, TEMPORARY_AUTH_FAILURE, UNKNOWN } @export var server_xmpp_name:String = "localhost": set = set_server_xmpp_name, get = get_server_xmpp_name func set_server_xmpp_name(value:String): server_xmpp_name = value func get_server_xmpp_name() -> String: return server_xmpp_name @export var port_number:int = 5222: set = set_port_number, get = get_port_number func set_port_number(value:int): port_number = value func get_port_number() -> int: return port_number @export var account_name:String = "undefined@localhost": set = set_account_name, get = get_account_name func set_account_name(value:String): account_name = value func get_account_name() -> String: return account_name @export var password:String = "undefined": set = set_password, get = get_password func set_password(value:String): password = value func get_password() -> String: return password @export var locale:String = "en": set = set_locale, get = get_locale func set_locale(value:String): locale = value func get_locale() -> String: return locale @export var xmppclient:bool = false: get: return xmppclient set(value): xmppclient = value const MAX_WAIT_CONNECTING:float = 60.0 const MAX_WAIT_MISSING_TLS:float = 1800.0 const MAX_WAIT_ACCOUNT_DISABLED:float = 900.0 const MAX_WAIT_NOT_AUTHORIZED:float = 18000.0 var try_connect:int = 0 var count_connecting_time:float = MAX_WAIT_CONNECTING var partial_stanza:String = "" var tcp_peer:StreamPeerTCP = StreamPeerTCP.new() var ssl_peer:StreamPeerTLS = StreamPeerTLS.new() var tgt_peer = null var status:StreamState = StreamState.END var xmpp_state = XMPPState.NONE var authentication_methods = [] var selected_mechanism_authenticate:String = "" var order_preference_mechanism_authenticate: Array = ['PLAIN'] #var order_preference_mechanism_authenticate: Array = ['DIGEST-MD5'] var banned_mechanism_authenticate:Array = ['DIGEST-MD5'] #DIGEST-MD5, PLAIN, SCRAM-SHA-512-PLUS, SCRAM-SHA-512, SCRAM-SHA-256-PLUS, SCRAM-SHA-256, SCRAM-SHA-1-PLUS, SCRAM-SHA-1, X-OAUTH2 var auhtentification_step:int = 0 var auhtentification_challenge:Array = [] const listcar:String = "AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn0123456789" #func reinit_stream(): # if ssl_peer != null: # ssl_peer.disconnect_from_stream() # else: # ssl_peer = StreamPeerTLS.new() # if tcp_peer != null: # tcp_peer.disconnect_from_host() # else: # tcp_peer = StreamPeerTCP.new() # tgt_peer = tcp_peer ## start_stream() # stream_status = self.StreamState.START func _init() -> void: var language = OS.get_locale() set_locale(language) tgt_peer = tcp_peer # reinit_stream() func _ready(): randomize() var n1:PackedByteArray = set_PackedByteArray("Mufasa:testrealm@host.com:Circle Of Life") var n2:PackedByteArray = hash_md5(n1) print("n2:", n2.hex_encode()) var exemple = digest_md5( "Mufasa", "Circle Of Life", "testrealm@host.com", "dcd98b7102dd2f0e8b11d0f600bfb0c093", "/dir/index.html", "auth", "00000001", "0a4f113b", "utf-8", "md5-sess" ) print("exemple:", exemple) exemple = digest_md5( "rob", "Circle Of Life", "cataclysm.cx", "OA6MG9tEQGm2hh", "xmpp/ cataclysm.cx", "auth", "00000001", "OA6MHXh6VqTrRk", "utf-8", "md5-sess" ) print("exemple 2:", exemple) func _process(delta) -> void: #print(server_xmpp_name, ":", port_number, " / get_status:" , tcp_peer.get_status(), " / count_connecting_time:", count_connecting_time, " / stream_status:", stream_status, " / xmpp_state:", XMPPState.keys()[xmpp_state]) if xmppclient == false: return # Wait MAX_WAIT_MISSING_TLS (s) if server can't negociate with TLS if xmpp_state == XMPPState.MISSING_TLS: if count_connecting_time >= MAX_WAIT_MISSING_TLS: xmpp_state = XMPPState.NONE else: count_connecting_time += delta elif xmpp_state == XMPPState.NOT_AUTHORIZED: if count_connecting_time >= MAX_WAIT_NOT_AUTHORIZED: xmpp_state = XMPPState.NONE else: count_connecting_time += delta elif xmpp_state == XMPPState.ACCOUNT_DISABLED: if count_connecting_time >= MAX_WAIT_ACCOUNT_DISABLED: xmpp_state = XMPPState.NONE else: count_connecting_time += delta elif xmpp_state == XMPPState.NONE: if count_connecting_time >= MAX_WAIT_CONNECTING: xmpp_state = XMPPState.TRY_CONNECT_SERVER if ssl_peer.get_status() != StreamPeerTLS.STATUS_DISCONNECTED: ssl_peer.disconnect_from_stream() if tcp_peer.get_status() != StreamPeerTCP.STATUS_NONE: tcp_peer.disconnect_from_host() else: count_connecting_time += delta elif xmpp_state == XMPPState.TRY_CONNECT_SERVER: if tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED: xmpp_state = XMPPState.CONNECTED_SERVER count_connecting_time = MAX_WAIT_CONNECTING elif count_connecting_time >= MAX_WAIT_CONNECTING: var res = tcp_peer.connect_to_host(server_xmpp_name, port_number) if res == OK: send_msg_debug.emit("Send connect_to_host : ok") #stream_status = self.StreamState.END #xmpp_state = XMPPState.CONNECTED_SERVER count_connecting_time = 0 else: send_msg_error.emit("Error to connect to XMPP server (return:%d)" % res) xmpp_state = XMPPState.NONE count_connecting_time = 0 else: if (tcp_peer.has_method("poll")): tcp_peer.poll() count_connecting_time += delta elif xmpp_state == XMPPState.CONNECTED_SERVER: if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: xmpp_state = XMPPState.NONE count_connecting_time = 0 return if count_connecting_time >= MAX_WAIT_CONNECTING: count_connecting_time = 0 send_msg_debug.emit("send_tcp_initialize_xmpp") send_tcp_initialize_xmpp() else: count_connecting_time += delta if (tcp_peer.has_method("poll")): tcp_peer.poll() if tcp_peer.get_available_bytes()>0: var response = tcp_peer.get_string(tcp_peer.get_available_bytes()) send_msg_debug.emit("Stream: response: " + response) response = remove_stream_header(response) if not analyze_error(response): if analyze_feature_starttls(response): xmpp_state = XMPPState.START_TLS else: count_connecting_time = 0 xmpp_state = XMPPState.MISSING_TLS elif xmpp_state == XMPPState.START_TLS: if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: xmpp_state = XMPPState.NONE count_connecting_time = 0 return if count_connecting_time >= MAX_WAIT_CONNECTING: count_connecting_time = 0 xmpp_state = XMPPState.NONE else: count_connecting_time += delta if (tcp_peer.has_method("poll")): tcp_peer.poll() if tcp_peer.get_available_bytes()>0: var response = tcp_peer.get_string(tcp_peer.get_available_bytes()) send_msg_debug.emit("Stream: response: " + response) response = remove_stream_header(response) if response.begins_with("= MAX_WAIT_CONNECTING: count_connecting_time = 0 xmpp_state = XMPPState.MISSING_TLS else: count_connecting_time += delta else: count_connecting_time = 0 xmpp_state = XMPPState.MISSING_TLS elif xmpp_state == XMPPState.STARTED_TLS: if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: xmpp_state = XMPPState.NONE count_connecting_time = 0 return if ssl_peer.get_status() != StreamPeerTLS.STATUS_CONNECTED: xmpp_state = XMPPState.NONE count_connecting_time = 0 return if count_connecting_time >= MAX_WAIT_CONNECTING: count_connecting_time = 0 send_msg_debug.emit("send_ssl_initialize_xmpp") send_ssl_initialize_xmpp() else: count_connecting_time += delta if (ssl_peer.has_method("poll")): ssl_peer.poll() if ssl_peer.get_available_bytes()>0: var response = ssl_peer.get_string(ssl_peer.get_available_bytes()) send_msg_debug.emit("Stream: response: " + response) response = remove_stream_header(response) if not analyze_error(response): if analyze_feature_mechanisms(response): if authentication_methods.size() > 0: count_connecting_time = MAX_WAIT_CONNECTING xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE #send_msg_debug.emit("AUTHENTICATE_STEP_1") # elif response.begins_with("= MAX_WAIT_CONNECTING: count_connecting_time = 0 negotiate_ssl_authenticate_plain() else: count_connecting_time += delta if (ssl_peer.has_method("poll")): ssl_peer.poll() if ssl_peer.get_available_bytes()>0: var response = ssl_peer.get_string(ssl_peer.get_available_bytes()) send_msg_debug.emit("Stream: response: " + response) response = remove_stream_header(response) var res:SASLReturn = get_result_authenticate(response) if res == SASLReturn.SUCCESS: count_connecting_time = 0 xmpp_state = XMPPState.AUTHENTICATED elif res == SASLReturn.NOT_AUTHORIZED: count_connecting_time = 0 xmpp_state = XMPPState.NOT_AUTHORIZED elif res == SASLReturn.ACCOUNT_DISABLED: count_connecting_time = 0 xmpp_state = XMPPState.ACCOUNT_DISABLED elif res == SASLReturn.NOT_AUTHORIZED: count_connecting_time = 0 xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE banned_mechanism_authenticate.append(selected_mechanism_authenticate) elif res == SASLReturn.INVALID_MECHANISM: count_connecting_time = 0 xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE banned_mechanism_authenticate.append(selected_mechanism_authenticate) else: count_connecting_time = 0 xmpp_state = XMPPState.SELECT_MECHANISM_AUTHENTICATE banned_mechanism_authenticate.append(selected_mechanism_authenticate) if xmpp_state == XMPPState.AUTHENTICATE_STEP_DIGEST_MD5: # https://datatracker.ietf.org/doc/html/rfc2831 if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: xmpp_state = XMPPState.NONE count_connecting_time = 0 return if count_connecting_time >= MAX_WAIT_CONNECTING: count_connecting_time = 0 negotiate_ssl_authenticate_digest_md5() else: count_connecting_time += delta if (ssl_peer.has_method("poll")): ssl_peer.poll() if ssl_peer.get_available_bytes()>0: var response = ssl_peer.get_string(ssl_peer.get_available_bytes()) send_msg_debug.emit("Stream: response: " + response) response = remove_stream_header(response) if response.begins_with("0: var response = ssl_peer.get_string(ssl_peer.get_available_bytes()) send_msg_debug.emit("Stream: response: " + response) start_stream() if xmpp_state == XMPPState.SENDED_START_STREAM: if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: xmpp_state = XMPPState.NONE count_connecting_time = 0 #send_msg_debug.emit("Stream: Lost flow 1") #send_msg_debug.emit("Stream: Lost flow 1 : " + str(tcp_peer.get_status()) ) return if (ssl_peer.has_method("poll")): ssl_peer.poll() if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED: xmpp_state = XMPPState.NONE count_connecting_time = 0 #send_msg_debug.emit("Stream: Lost flow 2 : " + str(tcp_peer.get_status()) ) return if ssl_peer.get_available_bytes()>0: var response = ssl_peer.get_string(ssl_peer.get_available_bytes()) send_msg_debug.emit("Stream: response: " + response) response = remove_stream_header(response) if not analyze_error(response): analyze_stream(response) # chat_connected.emit() func get_xml_data(response:String, nodes:Array) -> String: var node_name:String = "" var data:String = "" var parser:XMLParser = XMLParser.new() var cur:Array = [] parser.open_buffer(response.to_utf8_buffer()) while (parser.read() == OK): if (parser.get_node_type()== XMLParser.NODE_ELEMENT): node_name = parser.get_node_name() cur.append(node_name) print("node_name:", node_name) var attributes_dict = {} for idx in range(parser.get_attribute_count()): attributes_dict[parser.get_attribute_name(idx)] = parser.get_attribute_value(idx) print("The ", node_name, " element has the following attributes: ", attributes_dict) elif (parser.get_node_type()== XMLParser.NODE_TEXT): var correctpath:bool = true print(nodes.size() , " != ", cur.size()) if nodes.size() != cur.size(): continue for i in range(0, nodes.size()): print(cur[i] , " != ", nodes[i]) if cur[i] != nodes[i]: correctpath = false break if correctpath: data = parser.get_node_data() print("data:", data) return data elif (parser.get_node_type()== XMLParser.NODE_ELEMENT_END): cur.pop_back() return "" func get_xml_attribute(response:String, nodes:Array, attribute:String) -> String: var node_name:String = "" var data:String = "" var parser:XMLParser = XMLParser.new() var cur:Array = [] parser.open_buffer(response.to_utf8_buffer()) while (parser.read() == OK): if (parser.get_node_type()== XMLParser.NODE_ELEMENT): node_name = parser.get_node_name() cur.append(node_name) print("node_name:", node_name) var attributes_dict = {} for idx in range(parser.get_attribute_count()): if parser.get_attribute_name(idx) == attribute: return parser.get_attribute_value(idx) elif (parser.get_node_type()== XMLParser.NODE_ELEMENT_END): cur.pop_back() return "" func get_result_authenticate(response:String) -> SASLReturn: if response.begins_with(""): return SASLReturn.ABORTED elif response.contains(""): return SASLReturn.ACCOUNT_DISABLED if response.contains(""): return SASLReturn.CREDENTIALS_EXPIRED if response.contains(""): return SASLReturn.ENCRYPTION_REQUIRED if response.contains(""): return SASLReturn.INCORRECT_ENCODING if response.contains(""): return SASLReturn.INVALID_AUTHZID if response.contains(""): return SASLReturn.INVALID_MECHANISM if response.contains(""): return SASLReturn.MALFORMED_REQUEST if response.contains(""): return SASLReturn.MECHANISM_TOO_WEAK if response.contains(""): return SASLReturn.NOT_AUTHORIZED if response.contains(""): return SASLReturn.TEMPORARY_AUTH_FAILURE return SASLReturn.UNKNOWN func analyze_error(response:String) -> bool: if response.begins_with(" bool: if response.begins_with(" bool: if response.begins_with(" 0): var tmp:String = "" var sep:String = "" for item in authentication_methods: tmp += sep + item sep = ", " send_msg_debug.emit("Stream: authentication methods: " + tmp) #negotiate_ssl_sasl(authentication_methods) #stream_status = self.StreamState.AUTHENTICATE return true #negotiate_ssl_sasl(authentication_methods) return false func send_tcp_initialize_xmpp() -> void: """ Send the stream header. Needs to be done several times over stream negotiation. """ var message:String = "" + \ " " # " from=\"" + account_name + "\" " + \ # + locale + "'" + print(message) send_tcp_string(message) func send_ssl_initialize_xmpp() -> void: """ Send the stream header. Needs to be done several times over stream negotiation. """ var server_name = account_name.split("@")[-1] var message:String = "" + \ " " # " from=\"" + account_name + "\" " + \ # + locale + "'" + print(message) send_ssl_string(message) func select_mechanism_authenticate(authentication_methods : Array) -> void: selected_mechanism_authenticate = "NONE" for item in order_preference_mechanism_authenticate: if banned_mechanism_authenticate.has(item): continue elif authentication_methods.has(item): selected_mechanism_authenticate = item break func negotiate_ssl_authenticate_plain() -> void: send_msg_debug.emit("Stream: sending request for PLAIN") var msg:PackedByteArray = PackedByteArray() # [authzid] #msg += server_xmpp_name.to_ascii_buffer() # UTF8NUL msg.push_back(0) # authcid var t:String = account_name.split("@")[0] msg += t.to_ascii_buffer() # UTF8NUL msg.push_back(0) # passwd msg += password.to_ascii_buffer() var auth_account:String = Marshalls.raw_to_base64(msg) var request_sasl:String = "" + auth_account + "" send_ssl_string(request_sasl) func hash_md5(data:PackedByteArray ) -> PackedByteArray: var ctx = HashingContext.new() ctx.start(HashingContext.HASH_MD5) ctx.update(data) var res = ctx.finish() # Print the result as hex string and array. printt("hash_md5:", res.hex_encode(), Array(res)) return res func set_PackedByteArray(data:String) -> PackedByteArray: # var ret:PackedByteArray = PackedByteArray() # for item in data: # print(item) return data.to_ascii_buffer() func digest_md5(username:String, password:String, realm:String, nonce:String, uri:String, qop:String, nc:String, cnonce:String, charset:String, algorithm:String) -> String: if qop != "auth" and qop != "auth-int": send_msg_error.emit("Stream: Authenticate digest-md5, qop unknonw (%s)" % qop) return "" if charset != "utf-8": send_msg_error.emit("Stream: Authenticate digest-md5, charset unknonw (%s)" % charset) return "" if algorithm != "md5-sess": send_msg_error.emit("Stream: Authenticate digest-md5, algorithm unknonw (%s)" % algorithm) return "" #var serv_type:String = "xmpp" var X:String = username + ":" + realm + ":" + password print("X:", X) var Y:String = hash_md5(set_PackedByteArray(X)).hex_encode() print("Y:", Y) var A1:String = Y + ":" + nonce + ":" + cnonce print("A1:", A1) var A2:String = "AUTHENTICATE:" + uri print("A2:", A2) var HA1:String = hash_md5(set_PackedByteArray(A1)).hex_encode() print("HA1:", HA1) var HA2:String = hash_md5(set_PackedByteArray(A2)).hex_encode() print("HA2:", HA2) var KD:String = HA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + HA2 print("KD:", KD) var Z:String = hash_md5(set_PackedByteArray(KD)).hex_encode() print("Z:", Z) # # var HA1a:PackedByteArray = set_PackedByteArray(username + ":" + realm + ":" + password) # print("HA1a:", HA1a.get_string_from_ascii()) # #var HA1b:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + cnonce) # #print("HA1b:", HA1b.hex_encode()) # var HA1c:PackedByteArray = hash_md5(HA1a) # print("HA1c:", HA1c.hex_encode()) # var HA1d:PackedByteArray = hash_md5(set_PackedByteArray(HA1c.hex_encode() + ":" + nonce + ":" + cnonce)) # print("HA1d:", HA1d.hex_encode()) # var method:String = "AUTHENTICATE:" # var HA2:PackedByteArray = PackedByteArray() # if qop == "auth": # HA2 = hash_md5(set_PackedByteArray(method + ":" + uri)) # print("HA2:", HA2.hex_encode()) # else: # # HA2 = MD5(method:digestURI:MD5(entityBody)) # var entityBody:String = "" # var u:PackedByteArray = hash_md5(set_PackedByteArray(entityBody)) # #var t1:PackedByteArray = set_PackedByteArray(method + ":" + uri + ":") # var t:PackedByteArray = set_PackedByteArray(method + ":" + uri + ":" + u.hex_encode()) # HA2 = hash_md5(t) # #var i1:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":") # var response:PackedByteArray = hash_md5(set_PackedByteArray( # HA1d.hex_encode() + # ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + # HA2.hex_encode())) var ifrealm:String = "" if realm != "": ifrealm = ",realm=" + realm var msg: String = \ "username=\"" + username + "\"" + \ ifrealm + \ ",nonce=\"" + nonce + "\"" + \ ",cnonce=\"" + cnonce + "\"" + \ ",nc=" + nc + \ ",qop=" + qop + \ ",digest-uri=\"" + uri + "\"" + \ ",response=" + Z + \ ",charset=\"" + charset + "\"" print(msg) return Marshalls.utf8_to_base64(msg) func negotiate_ssl_authenticate_digest_md5() -> bool: """ NE MARCHE PAS !!!!!! """ send_msg_debug.emit("Stream: sending request for DIGEST-MD5 (step:%d)" % auhtentification_step) if auhtentification_step == 0: var request_sasl:String = "" + "" send_ssl_string(request_sasl) auhtentification_step += 1 return true elif auhtentification_step == 1: var tmp:String = Marshalls.base64_to_utf8(auhtentification_challenge[0]) var tab:Array = tmp.split(",") var realm:String = "" var nonce:String = "" var qop:String = "" var charset:String = "" var algorithm:String = "" var cnonce:String = "" var nc:String = "00000001" var username:String = account_name.split("@")[0] #var username:String = account_name print(str(tab)) for item in tab: var tab2:Array = item.split("=") var key:String = tab2[0] var value:String = tab2[1] if key == "realm": realm = value.trim_prefix('"').trim_suffix('"') if key == "nonce": nonce = value.trim_prefix('"').trim_suffix('"') elif key == "qop": qop = value.trim_prefix('"').trim_suffix('"') elif key == "charset": charset = value elif key == "algorithm": algorithm = value print(key," = ", value) print(tmp, " ", qop, " ", charset, " ",nonce) if qop != "auth" and qop != "auth-int": send_msg_error.emit("Stream: Authenticate digest-md5, qop unknonw (%s)" % qop) return false if charset != "utf-8": send_msg_error.emit("Stream: Authenticate digest-md5, charset unknonw (%s)" % charset) return false if algorithm != "md5-sess": send_msg_error.emit("Stream: Authenticate digest-md5, algorithm unknonw (%s)" % algorithm) return false for car in nonce: var v:String = listcar[randi() % listcar.length()] cnonce += v print(cnonce) var auth_account = digest_md5(username,password,realm,nonce, "xmpp/" + server_xmpp_name, qop, nc, cnonce, charset, algorithm) # print(tmp) # # username:String, password:String, realm:String, nonce:String, uri:String, qop:String, nc:String, cnonce:String, charset:String, algorithm:String) -> String: # # var serv_type:String = "xmpp" # #var HA1a:PackedStringArray = PackedStringArray([username ,":", realm , ":", password]) # var HA1a:PackedByteArray = set_PackedByteArray(username + ":" + realm + ":" + password) # # #var titi = username + ":" + realm + ":" + password # #print("titi:", titi) # #HA1a.push_back(username + ":" + realm + ":" + password) # #var o1:PackedByteArray = set_PackedByteArray(username + ":" + realm + ":" + password) # #print("o1:", o1.get_string_from_ascii()) # print("HA1a:", HA1a.hex_encode()) # var HA1b:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + cnonce) # print("HA1b:", HA1b.hex_encode()) # var HA1c:PackedByteArray = hash_md5(set_PackedByteArray(HA1a.hex_encode())) # var HA1d:PackedByteArray = hash_md5(set_PackedByteArray(HA1c.hex_encode() + HA1b.hex_encode())) # var method:String = "AUTHENTICATE:" # var digest_uri:String = serv_type + "/" + server_xmpp_name # var HA2:PackedByteArray # if qop == "auth": # #var t:PackedByteArray = set_PackedByteArray(method + ":" + digest_uri) # HA2 = hash_md5(set_PackedByteArray(method + ":" + digest_uri)) # else: # # HA2 = MD5(method:digestURI:MD5(entityBody)) # var entityBody:String = "" # var u:PackedByteArray = hash_md5(set_PackedByteArray(entityBody)) # #var t1:PackedByteArray = set_PackedByteArray(method + ":" + digest_uri + ":") # var t:PackedByteArray = set_PackedByteArray(method + ":" + digest_uri + ":" + u.hex_encode()) # HA2 = hash_md5(t) # #var i1:PackedByteArray = set_PackedByteArray(":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":") # var response:PackedByteArray = hash_md5(set_PackedByteArray( # HA1d.hex_encode() + # ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + # HA2.hex_encode())) # var ifrealm:String = "" # if realm != "": # ifrealm = ",realm=" + realm # var msg: PackedByteArray = set_PackedByteArray( # "username=\"" + username + "\"" + # ifrealm + # ",nonce=\"" + nonce + "\"" + # ",cnonce=\"" + cnonce + "\"" + # ",nc=" + nc + # ",qop=" + qop + # ",digest-uri=\"" + digest_uri + "\"" + # ",response=" + response.hex_encode() + # ",charset=\"" + charset + "\"" # ) # + response ## charset=utf-8,username="chris",realm="elwood.innosoft.com", ## nonce="OA6MG9tEQGm2hh",nc=00000001,cnonce="OA6MHXh6VqTrRk", ## digest-uri="imap/elwood.innosoft.com", ## response=d388dad90d4bbd760a152321f2143af7,qop=auth # print("msg:", msg.get_string_from_ascii()) ## var msf2:PackedStringArray = PackedStringArray() ## var mystring:String = 'username="rob",realm="cataclysm.cx",nonce="OA6MG9tEQGm2hh",cnonce="OA6MHXh6VqTrRk",nc=00000001,qop=auth,digest-uri="xmpp/cataclysm.cx",response=d388dad90d4bbd760a152321f2143af7,charset=utf-8,authzid="rob@cataclysm.cx/myResource"'.strip_edges() ## msf2.push_back(mystring) ## var sol:String = "dXNlcm5hbWU9InJvYiIscmVhbG09ImNhdGFjbHlzbS5jeCIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixjbm9uY2U9Ik9BNk1IWGg2VnFUclJrIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2NhdGFjbHlzbS5jeCIscmVzcG9uc2U9ZDM4OGRhZDkwZDRiYmQ3NjBhMTUyMzIxZjIxNDNhZjcsY2hhcnNldD11dGYtOCxhdXRoemlkPSJyb2JAY2F0YWNseXNtLmN4L215UmVzb3VyY2Ui" ## print("---", "".join(msf2)) ## print("msf2:", Marshalls.raw_to_base64(msf2.to_byte_array())) ## print("msf2:", Marshalls.utf8_to_base64("".join(msf2))) ## print("msf2:", Marshalls.utf8_to_base64(mystring)) ## if sol != Marshalls.raw_to_base64(msf2.to_byte_array()): ## print("********************************************** FAILED 1" ) ## if sol != Marshalls.utf8_to_base64(mystring): ## print("********************************************** FAILED 2") ## if sol != Marshalls.utf8_to_base64("".join(msf2)): ## print("********************************************** FAILED 3") # var auth_account:String = Marshalls.utf8_to_base64(msg.get_string_from_ascii()) var request_sasl:String = "" + auth_account + "" send_ssl_string(request_sasl) auhtentification_step += 1 return true return false func negotiate_ssl_sasl(authentication_methods : Array) -> bool: if ( authentication_methods.has("PLAIN")): send_msg_debug.emit("Stream: sending request for plain") var msg:PackedByteArray = PackedByteArray() msg.push_back(0) var t = account_name.split("@")[0] msg += t.to_ascii_buffer() #msg += conv_string_to_PackedByteArray(account_name.split("@")[0]) msg.push_back(0) msg += password.to_ascii_buffer() var auth_account:String = Marshalls.raw_to_base64(msg) var request_sasl:String = "" + auth_account + "" send_ssl_string(request_sasl) return true else: send_msg_error.emit("Impossible to authenticate (unknown protocol)") return false # # # #func connect_to_server_xmpp() -> void: # var res = tcp_peer.connect_to_host(server_xmpp_name, port_number) # if res == OK: # count_connecting_time = 0 # stream_status = self.StreamState.END # set_process(true) # tgt_peer = tcp_peer #func _process_old(delta) -> void: # print(server_xmpp_name, ":", port_number, " / get_status:" , tcp_peer.get_status(), " / count_connecting_time:", count_connecting_time, " / stream_status:", stream_status) # if tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED: # print("STATUS_CONNECTED:", delta) # if (tgt_peer.has_method("poll")): # tgt_peer.poll() # if tgt_peer.get_available_bytes()>0: # var response = tgt_peer.get_string(tgt_peer.get_available_bytes()) # send_msg_debug.emit("Stream: response: " + response) # if stream_status == self.StreamState.STANZA: # #collect_stanza(remove_stream_header(response)) # print("collect_stanza") # else: # print("stream_process") # stream_process(remove_stream_header(response)) ## print(remove_stream_header(response)) # elif stream_status == self.StreamState.END: # reinit_stream() # #start_stream() # #stream_status = self.StreamState.START # count_connecting_time = 0 # if tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTING: # print("STATUS_CONNECTING:", delta) # count_connecting_time += delta # if (tgt_peer.has_method("poll")): # tgt_peer.poll() # if count_connecting_time > 60: # (1 -> 1s) if it took more than 1s to connect, error # print("**** Stream: Stuck connecting, will now disconnect") # Global.msg_error("Stream: Stuck connecting, will now disconnect", []) # send_msg_debug.emit("Stream: Stuck connecting, will now disconnect") # tgt_peer.disconnect_from_host() #interrupts connection to nothing # set_process(false) # stop listening for packets # try_connect = 20 # stream_status = self.StreamState.END # reinit_stream() # if tgt_peer.get_status() == StreamPeerTCP.STATUS_NONE and xmppclient: # print("connect_to_server:", xmppclient) # connect_to_server() # xmppclient = false func connect_to_server(): """ Connect to the server ip and port, and start checking whether there's stream info yet. """ if tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED: pass if try_connect == 0 and tcp_peer.get_status() == StreamPeerTCP.STATUS_NONE: #print("-> ", server_ip, ":", port_number, "/" , tcp_peer.get_status()) var res = tcp_peer.connect_to_host(server_xmpp_name, port_number) if res == OK: count_connecting_time = 0 stream_status = self.StreamState.END set_process(true) tgt_peer = tcp_peer else: try_connect = 20 else: try_connect -= 1 func send_string(stanza:String) ->void: """ Send a string in the appropriate encoding. """ send_msg_debug.emit("Sending data: '%'".format([stanza], "%")) tgt_peer.put_data(stanza.to_utf8_buffer()) func send_tcp_string(stanza:String) ->void: """ Send a string in the appropriate encoding. """ send_msg_debug.emit("Sending data: '%'".format([stanza], "%")) tcp_peer.put_data(stanza.to_utf8_buffer()) func send_ssl_string(stanza:String) ->void: """ Send a string in the appropriate encoding. """ send_msg_debug.emit("Sending data: '%'".format([stanza], "%")) ssl_peer.put_data(stanza.to_utf8_buffer()) func conv_string_to_PackedByteArray(message:String) -> PackedByteArray: return message.to_ascii_buffer() #func send_PackedByteArray(message:PackedByteArray) -> void: # """ Send a PackedByteArray """ # send_msg_debug.emit("Sending data: '" + message.get_string_from_utf8() + "'") # tgt_peer.put_data(message) #func start_stream() -> void: # """ Send the stream header. # Needs to be done several times over stream negotiation. # """ # var server_name = account_name.split("@")[-1] # var message:String = "" + \ # " " # # " from=\"" + account_name + "\" " + \ # # + locale + "'" + # print(message) # send_string(message) func remove_stream_header(text :String) -> String: var index = 0 if text.begins_with("") text = text.substr(index+2).strip_edges() # strip stream header var rg = RegEx.new() rg.compile("<\\s?(stream|stream:stream)\\s") var result = rg.search(text) if result: send_msg_debug.emit("Stream: Response header received") index = text.find(">", result.get_end()) text = text.substr(index+1) return text #func stream_process(response :String = "") -> void: # """ Try to authenticate using SASL. We can currently only do plain. # For SCRAM based methods, we need HMAC at the very least. # """ # # if (!response.length() == 0 and stream_status != self.StreamState.STANZA): # if response.begins_with(" 0): # var tmp:String = "" # var sep:String = "" # for item in authentication_methods: # tmp += sep + item # sep = ", " # send_msg_debug.emit("Stream: authentication methods: " + tmp) # negotiate_sasl(authentication_methods) # stream_status = self.StreamState.AUTHENTICATE # # else: # if stream_status == StreamState.TLS: # if response.begins_with(" bool: # #var ssl_peer = StreamPeerTLS.new() # # I am unsure how to validate the ssl certificate for an unknown host? # #var ssl_succes = FAILED # var options:TLSOptions = TLSOptions.client_unsafe() # print("accept_stream") # #var ssl_succes = ssl_peer.accept_stream(tcp_peer, options) # var ssl_succes = ssl_peer.connect_to_stream(tcp_peer, "localhost", options) # print("resultat:", ssl_succes) # if ssl_succes == OK: # send_msg_debug.emit("Stream: switched to SSL!") # print("get_status:", ssl_peer.get_status()) # # Wait connection done # while ssl_peer.get_status() == StreamPeerTLS.STATUS_HANDSHAKING: # ssl_peer.poll() # print("get_status:", ssl_peer.get_status()) # tgt_peer = ssl_peer # start_stream() # print("get_status:", ssl_peer.get_status()) # print("-----") # #stream_status == StreamState.START # return true # else: # send_msg_error.emit("SSL failed, error %".format([ssl_succes], "%")) # return false # # #func negotiate_sasl(authentication_methods : Array) -> void: # if ( authentication_methods.has("PLAIN")): # send_msg_debug.emit("Stream: sending request for plain") # var msg:PackedByteArray = PackedByteArray() # msg.push_back(0) # var t = account_name.split("@")[0] # msg += t.to_ascii_buffer() # #msg += conv_string_to_PackedByteArray(account_name.split("@")[0]) # msg.push_back(0) # msg += password.to_ascii_buffer() # var auth_account:String = Marshalls.raw_to_base64(msg) # var request_sasl:String = "" + auth_account + "" # send_string(request_sasl) # else: # send_msg_error.emit("Impossible to authenticate (unknown protocol)") # end_stream() # #func end_stream() -> void: # """ End the stream """ # send_msg_debug.emit("Stream: Ending stream") # send_string("") # if tcp_peer.has_method("disconnect_from_stream"): # tcp_peer.disconnect_from_stream() # else: # tcp_peer.disconnect_from_host() # set_process(false) # stream_status = StreamState.END # # # func analyze_stream(msg:String) -> void: partial_stanza += msg if partial_stanza.is_empty(): return var complete_stanza = true while complete_stanza == true: var parser = XMLParser.new() if partial_stanza.length() == 0: return parser.open_buffer( set_PackedByteArray(partial_stanza) ) var element_name = "" var node_offset = 0 if parser.read() == OK and parser.get_node_type() == XMLParser.NODE_ELEMENT: element_name = parser.get_node_name() if parser.is_empty(): node_offset = partial_stanza.length() if parser.read() == OK: node_offset = parser.get_node_offset() while parser.read() == OK and node_offset == 0: if parser.get_node_type() == XMLParser.NODE_ELEMENT_END: if parser.get_node_name() == element_name: if parser.read() == OK: node_offset = parser.get_node_offset() else: node_offset = partial_stanza.length() if node_offset > 0: # We have found a complete stanza. var new_stanza:String = partial_stanza.left(node_offset) if node_offset == partial_stanza.length(): partial_stanza = "" else: partial_stanza = partial_stanza.right(-node_offset) #emit_signal("new_stanza", new_stanza) print("new_stanza:", new_stanza) else: complete_stanza = false func escape_html(msg:String) -> String: var tmp = msg tmp.replace("&", "&") tmp.replace("<", "<") tmp.replace(">", ">") tmp.replace("'", "'") tmp.replace("\"", """) return tmp func send_message_to_old(message, to): if xmpp_state != XMPPState.AUTHENTICATED: return var msg:String = "\n" + \ " " + escape_html(message) + "\n" + \ "" send_ssl_string(msg) func send_message_to_1(message, to): if xmpp_state != XMPPState.AUTHENTICATED: return var msg:String = "\n" + \ " " + escape_html(message) + "\n" + \ "" send_ssl_string(msg) func send_message_to(message, to): if xmpp_state != XMPPState.AUTHENTICATED: return var msg:String = "\n" + \ " " + escape_html(message) + "\n" + \ "" send_ssl_string(msg) func start_stream(): if xmpp_state != XMPPState.AUTHENTICATED: return var msg:String = "" + \ "\n" + \ "" send_ssl_string(msg) xmpp_state = XMPPState.SENDED_START_STREAM