2727 API_ENDPOINT ,
2828 DEFAULT_TIMEOUT ,
2929 DEFAULT_USER_AGENT ,
30+ UINT_MAX ,
3031 XMO_ACCESS_RESTRICTION_ERR ,
3132 XMO_AUTHENTICATION_ERR ,
3233 XMO_INVALID_SESSION_ERR ,
@@ -91,10 +92,10 @@ def __init__(
9192 self .username = username
9293 self .authentication_method = authentication_method
9394 self .password = password
95+ self ._current_nonce = None
9496 self ._password_hash = self .__generate_hash (password )
9597 self .protocol = "https" if ssl else "http"
9698
97- self ._current_nonce = None
9899 self ._server_nonce = ""
99100 self ._session_id = 0
100101 self ._request_id = - 1
@@ -126,14 +127,31 @@ async def close(self) -> None:
126127 """Close the websession."""
127128 await self .session .close ()
128129
129- def __generate_nonce (self ):
130+ def __generate_nonce (self , upper_limit = 500000 ):
130131 """Generate pseudo random number (nonce) to avoid replay attacks."""
131- self ._current_nonce = math .floor (random .randrange (0 , 500000 ))
132+ self ._current_nonce = math .floor (random .randrange (0 , upper_limit ))
132133
133134 def __generate_request_id (self ):
134135 """Generate sequential request ID."""
135136 self ._request_id += 1
136137
138+ def __generate_md5_nonce_hash (self ):
139+ """Build MD5 with nonce hash token. UINT_MAX is hardcoded in the firmware."""
140+
141+ def md5 (input_string ):
142+ return hashlib .md5 (input_string .encode ()).hexdigest ()
143+
144+ n = (
145+ self .__generate_nonce (UINT_MAX )
146+ if self ._current_nonce is None
147+ else self ._current_nonce
148+ )
149+ f = 0
150+ l_nonce = ""
151+ ha1 = md5 (self .username + ":" + l_nonce + ":" + md5 (self .password ))
152+
153+ return md5 (ha1 + ":" + str (f ) + ":" + str (n ) + ":JSON:/cgi/json-req" )
154+
137155 def __generate_hash (self , value , authentication_method = None ):
138156 """Hash value with selected encryption method and return HEX value."""
139157 auth_method = authentication_method or self .authentication_method
@@ -146,6 +164,9 @@ def __generate_hash(self, value, authentication_method=None):
146164 if auth_method == EncryptionMethod .SHA512 :
147165 return hashlib .sha512 (bytes_object ).hexdigest ()
148166
167+ if auth_method == EncryptionMethod .MD5_NONCE :
168+ return self .__generate_md5_nonce_hash ()
169+
149170 return value
150171
151172 def __get_credential_hash (self ):
0 commit comments