Skip to content

Commit 1e00ed4

Browse files
committed
addd 'bcrypt' support (simplified)
1 parent 71f6b04 commit 1e00ed4

File tree

1 file changed

+43
-64
lines changed
  • custom_components/goecharger_api2/pygoecharger_ha

1 file changed

+43
-64
lines changed

custom_components/goecharger_api2/pygoecharger_ha/__init__.py

Lines changed: 43 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,10 @@ def _ws_compute_hashed_password(self, hash_type: str, password: str, serial: str
477477
# initially found @ https://github.com/joscha82/wattpilot/issues/46#issuecomment-3289810024
478478
# and then took it from wattpilot!
479479
# https://github.com/mk-maddin/wattpilot-HA/blob/master/custom_components/wattpilot/wattpilot/src/wattpilot/__init__.py#L498
480-
return self.__bcrypt_hash_password(password, serial).encode()
480+
iterations = 8
481+
password_hash = hashlib.sha256(password.encode()).hexdigest()
482+
salt = f"$2a${iterations:02d}${bcryptjs_encode_base64(serial, 16)}"
483+
return bcrypt.hashpw(password_hash.encode(), salt.encode())[len(salt):]
481484

482485
return None
483486

@@ -767,68 +770,44 @@ def extract_ws_message_data(self, data:dict):
767770

768771
return new_data_arrived
769772

770-
def __bcryptjs_base64_encode(self,b: bytes, length: int) -> str:
771-
BASE64_CODE = list("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
772-
off = 0
773-
rs = []
774-
775-
if length <= 0 or length > len(b):
776-
raise ValueError(f"Illegal len: {length}")
777-
778-
while off < length:
779-
c1 = b[off] & 0xff
780-
off += 1
781-
rs.append(BASE64_CODE[(c1 >> 2) & 0x3f])
782-
c1 = (c1 & 0x03) << 4
783-
if off >= length:
784-
rs.append(BASE64_CODE[c1 & 0x3f])
785-
break
773+
@staticmethod
774+
def bcryptjs_base64_encode(b: bytes, length: int) -> str:
775+
BASE64_CODE = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
786776

787-
c2 = b[off] & 0xff
788-
off += 1
789-
c1 |= (c2 >> 4) & 0x0f
790-
rs.append(BASE64_CODE[c1 & 0x3f])
791-
c1 = (c2 & 0x0f) << 2
792-
if off >= length:
793-
rs.append(BASE64_CODE[c1 & 0x3f])
794-
break
777+
if not 0 < length <= len(b):
778+
raise ValueError(f"Illegal len: {length}")
779+
780+
rs = []
781+
i = 0
782+
783+
while i < length:
784+
c1 = (b[i] & 0x03) << 4
785+
rs.append(BASE64_CODE[b[i] >> 2])
786+
i += 1
787+
788+
if i >= length:
789+
rs.append(BASE64_CODE[c1])
790+
break
791+
792+
rs.append(BASE64_CODE[c1 | (b[i] >> 4)])
793+
c1 = (b[i] & 0x0f) << 2
794+
i += 1
795+
796+
if i >= length:
797+
rs.append(BASE64_CODE[c1])
798+
break
799+
800+
rs.append(BASE64_CODE[c1 | (b[i] >> 6)])
801+
rs.append(BASE64_CODE[b[i] & 0x3f])
802+
i += 1
803+
804+
return "".join(rs)
805+
806+
@staticmethod
807+
def bcryptjs_encode_base64(s: str, length: int) -> str:
808+
if not s.isdigit():
809+
_LOGGER.warning(f"bcryptjs_encode_base64(): check serial string - should be digits only: {s}")
810+
raise ValueError(f"Check serial string - should be digits only: {s}")
795811

796-
c2 = b[off] & 0xff
797-
off += 1
798-
c1 |= (c2 >> 6) & 0x03
799-
rs.append(BASE64_CODE[c1 & 0x3f])
800-
rs.append(BASE64_CODE[c2 & 0x3f])
801-
802-
return "".join(rs)
803-
804-
def __bcryptjs_encode_base64(self, s: str, length: int) -> str:
805-
if s.isdigit(): #numeric only serial
806-
vals = [ord(ch) - ord('0') for ch in s]
807-
b = bytes([0] * (length - len(vals)) + vals)
808-
else: #not sure about serials in future - fallback
809-
_LOGGER.warning(f"__bcryptjs_encodeBase64: check serial string - should be digits only: {s}")
810-
raise ValueError(f"Check serial string - should be digits only: {s}")
811-
812-
return self.__bcryptjs_base64_encode(b,length)
813-
814-
def __bcrypt_hash_password(self, password, serial, iterations=8) -> str:
815-
password_hash_sha256 = hashlib.sha256(password.encode('utf-8')).hexdigest()
816-
serial_b64 = self.__bcryptjs_encode_base64(serial, 16)
817-
818-
# don't remove this (or do not simplify this to
819-
# salt = ["$2a$"]
820-
salt = []
821-
salt.append("$2a$")
822-
823-
if iterations < 10:
824-
salt.append("0")
825-
salt.append(str(iterations))
826-
salt.append("$")
827-
salt.append(serial_b64)
828-
salt=''.join(salt)
829-
bsalt = salt.encode("utf-8")
830-
bpassword = password_hash_sha256.encode('utf-8')
831-
pwhash = bcrypt.hashpw(bpassword, bsalt)
832-
salt_length = len(salt)
833-
pwhash_sub = pwhash[salt_length:].decode('ascii')
834-
return pwhash_sub
812+
b = bytes([0] * (length - len(s)) + [int(ch) for ch in s])
813+
return bcryptjs_base64_encode(b, length)

0 commit comments

Comments
 (0)