Closed arekm closed 3 years ago
enc is missing in ''application/x-www-form-urlencoded; charset=UTF-8'
It should be: 'application/x-www-form-urlencoded; charset=UTF-8;enc'
@arekm my router is using ''application/x-www-form-urlencoded; charset=UTF-8' and additional header:
headers['encrypt_transmit'] = 'encrypt_transmit'
And it works, it is not working for your router in this configuration?
Mine uses 'application/x-www-form-urlencoded; charset=UTF-8;enc' and doesn't send 'encrypt_transmit'. Without ';enc' I'm getting "huawei_lte_api.exceptions.ResponseErrorException: 100001: Unknown" on "POST /api/dialup/profiles".
Also it actually doesn't do GET api/webserver/publickey but instead takes rsa components from
POST api/user/challenge_login
<?xml version="1.0" encoding="UTF-8"?><request><username>admin</username><firstnonce>c240....60fb</firstnonce><mode>1</mode></request>
which returns
<?xml version="1.0" encoding="UTF-8"?><response><salt>09....0c2</salt><modeselected>1</modeselected><servernonce>c240....KtPCW0dvISb0</servernonce><newType>0</newType><iterations>1000</iterations></response>
then
POST api/user/authentication_login
<?xml version="1.0" encoding="UTF-8"?><request><clientproof>b7071a3....0b5508</clientproof><finalnonce>c240c....Sb0</finalnonce></request>
which returns:
<?xml version="1.0" encoding="UTF-8"?><response><serversignature>45151a0c0e...a11a5baae9d992</serversignature><rsapubkeysignature>b01...72c</rsapubkeysignature><rsae>01...01</rsae><rsan>c2767b...4b</rsan></response>
but api/webserver/publickey also exists on my router and returns the same rsa e/n, so most likely a way to get e/n doesn't matter. Also user.state_login() returns the same values in huawei lte api script that api/user/state-login from browser does. So I guess both ways of logging end up in the same "logged in" state.
Anyway adding ';enc' and
client.dial_up.set_default_profile(2)
ends up with
http.client.RemoteDisconnected: Remote end closed connection without response
Not sure what it doesn't like. Still playing with it.
Encryption is done a bit differently for my router:
diff --git a/huawei_lte_api/Session.py b/huawei_lte_api/Session.py
index 02f6b44..f5e6dbd 100644
--- a/huawei_lte_api/Session.py
+++ b/huawei_lte_api/Session.py
@@ -145,12 +145,13 @@ class Session:
return urllib.parse.urljoin(self.url + '{}/'.format(prefix), endpoint)
def _encrypt_data(self, data: bytes) -> bytes:
+ rsapading = self._get_rsa_pading()
pubkey_data = self._get_encryption_key()
rsa_e = pubkey_data.get('encpubkeye')
rsa_n = pubkey_data.get('encpubkeyn')
if not rsa_n or not rsa_e:
raise Exception('No pub key was found')
- return Tools.rsa_encrypt(rsa_e, rsa_n, data)
+ return Tools.rsa_encrypt(rsa_e, rsa_n, data, rsapading)
def post_get(self,
endpoint: str,
@@ -186,7 +187,7 @@ class Session:
-> Union[GetResponseType, SetResponseType]:
headers = {
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' if is_encrypted else 'application/xml'
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8;enc' if is_encrypted else 'application/xml'
}
if is_encrypted:
@@ -276,6 +277,10 @@ class Session:
self.encryption_key = self.get('webserver/publickey')
return self.encryption_key
+ def _get_rsa_pading(self) -> int:
+ state_login = self.get('user/state-login')
+ return int(state_login['rsapadingtype']) if 'rsapadingtype' in state_login else 0
+
def close(self) -> None:
self.requests_session.close()
diff --git a/huawei_lte_api/Tools.py b/huawei_lte_api/Tools.py
index 5589d8c..765f44f 100644
--- a/huawei_lte_api/Tools.py
+++ b/huawei_lte_api/Tools.py
@@ -3,7 +3,7 @@ from typing import Union
from binascii import hexlify
import math
import base64
-from Cryptodome.Cipher import PKCS1_v1_5
+from Cryptodome.Cipher import PKCS1_v1_5, PKCS1_OAEP
from Cryptodome.PublicKey.RSA import construct
@@ -28,16 +28,21 @@ class Tools:
return data
@staticmethod
- def rsa_encrypt(rsa_e: str, rsa_n: str, data: bytes) -> bytes:
+ def rsa_encrypt(rsa_e: str, rsa_n: str, data: bytes, rsapading: int = 0) -> bytes:
modulus = int(rsa_n, 16)
exponent = int(rsa_e, 16)
b64data = base64.b64encode(data)
pubkey = construct((modulus, exponent))
- cipher = PKCS1_v1_5.new(pubkey)
- blocks = int(math.ceil(len(b64data) / 245.0))
+ if rsapading == 1:
+ num = 214
+ cipher = PKCS1_OAEP.new(pubkey)
+ else:
+ num = 245
+ cipher = PKCS1_v1_5.new(pubkey)
+ blocks = int(math.ceil(len(b64data) / float(num)))
result_chunks = []
for i in range(blocks):
- block = b64data[i * 245:(i + 1) * 245]
+ block = b64data[i * num:(i + 1) * num]
d = cipher.encrypt(block)
result_chunks.append(d)
result = hexlify(b''.join(result_chunks))
note: huawei uses 'rsapadingtype' in state-login result but probably the rest of code should use proper name 'rsapaddingtype' (I used original huawei name, with typo).
Also ";enc" should be conditional (but actually non encrypted requests do work fine on this router even if ";enc" is there)
@arekm Ok, your patch should be part of 1.5.1 release
Thanks.
diff --git a/huawei_lte_api/Session.py b/huawei_lte_api/Session.py
index e7c43a3..15aa3f4 100644
--- a/huawei_lte_api/Session.py
+++ b/huawei_lte_api/Session.py
@@ -186,12 +186,13 @@ class Session:
is_encrypted: bool = False) \
-> Union[GetResponseType, SetResponseType]:
- headers = {
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8;enc' if is_encrypted else 'application/xml'
- }
+ headers = {}
if is_encrypted:
+ headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8;enc'
headers['encrypt_transmit'] = 'encrypt_transmit'
+ else:
+ headers['Content-Type'] = 'application/xml'
if self.request_verification_tokens:
if len(self.request_verification_tokens) > 1:
dialup/profiles seems to be able to be used for profile modification (including changing default profile number).
POST api/dialup/profiles
Delete - if we want delete profile nr X SetDefault - if we want to set profile X as default
Info from http://forum.jdtech.pl/Watek-hilink-api-dla-urzadzen-huawei#dialup
On my huawei 5g cpe 2 just doing
doesn't work unfortunately
huawei_lte_api.exceptions.ResponseErrorException: 100001: Unknown
but tracking router UI in inspector and when changing default profile (which is just editing profile with one checkbox active) uses POST on api/dialup/profiles sending
which is rsa encrypted
with keys from
api/webserver/publickey
https://adventuresinadigitalland.blogspot.com/2019/03/huawei-modemrouter-sending-encrypted.html