pyezvizapi 1.0.4.1__py3-none-any.whl → 1.0.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyezvizapi might be problematic. Click here for more details.
- pyezvizapi/utils.py +82 -19
- {pyezvizapi-1.0.4.1.dist-info → pyezvizapi-1.0.4.2.dist-info}/METADATA +1 -1
- {pyezvizapi-1.0.4.1.dist-info → pyezvizapi-1.0.4.2.dist-info}/RECORD +7 -7
- {pyezvizapi-1.0.4.1.dist-info → pyezvizapi-1.0.4.2.dist-info}/WHEEL +0 -0
- {pyezvizapi-1.0.4.1.dist-info → pyezvizapi-1.0.4.2.dist-info}/licenses/LICENSE +0 -0
- {pyezvizapi-1.0.4.1.dist-info → pyezvizapi-1.0.4.2.dist-info}/licenses/LICENSE.md +0 -0
- {pyezvizapi-1.0.4.1.dist-info → pyezvizapi-1.0.4.2.dist-info}/top_level.txt +0 -0
pyezvizapi/utils.py
CHANGED
|
@@ -153,39 +153,102 @@ def decrypt_image(input_data: bytes, password: str) -> bytes:
|
|
|
153
153
|
bytes: Decrypted image data
|
|
154
154
|
|
|
155
155
|
"""
|
|
156
|
-
if len(input_data) < 48:
|
|
157
|
-
raise PyEzvizError("Invalid image data")
|
|
158
|
-
|
|
159
|
-
# check header
|
|
160
156
|
header_len = len(HIK_ENCRYPTION_HEADER)
|
|
157
|
+
min_length = header_len + 32 # header + md5 hash
|
|
161
158
|
|
|
162
|
-
if input_data
|
|
159
|
+
if len(input_data) < min_length:
|
|
160
|
+
raise PyEzvizError("Invalid image data")
|
|
161
|
+
|
|
162
|
+
header_index = input_data.find(HIK_ENCRYPTION_HEADER)
|
|
163
|
+
if header_index == -1:
|
|
163
164
|
_LOGGER.debug("Image header doesn't contain %s", HIK_ENCRYPTION_HEADER)
|
|
164
165
|
return input_data
|
|
165
166
|
|
|
166
|
-
|
|
167
|
+
if header_index:
|
|
168
|
+
_LOGGER.debug("Image header found at offset %s, trimming preamble", header_index)
|
|
169
|
+
input_data = input_data[header_index:]
|
|
170
|
+
if len(input_data) < min_length:
|
|
171
|
+
raise PyEzvizError("Invalid image data after trimming preamble")
|
|
172
|
+
|
|
173
|
+
hash_end = header_len + 32
|
|
174
|
+
blocks = _split_encrypted_blocks(input_data, header_len, min_length)
|
|
175
|
+
if not blocks:
|
|
176
|
+
raise PyEzvizError("Invalid image data")
|
|
177
|
+
|
|
178
|
+
decrypted_parts = [
|
|
179
|
+
_decrypt_single_block(block, password, header_len, hash_end) for block in blocks
|
|
180
|
+
]
|
|
181
|
+
if len(decrypted_parts) > 1:
|
|
182
|
+
_LOGGER.debug("Decrypted %s concatenated image blocks", len(decrypted_parts))
|
|
183
|
+
return b"".join(decrypted_parts)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _split_encrypted_blocks(
|
|
187
|
+
data: bytes, header_len: int, min_length: int
|
|
188
|
+
) -> list[bytes]:
|
|
189
|
+
"""Split concatenated hikencodepicture segments into individual blocks."""
|
|
190
|
+
blocks: list[bytes] = []
|
|
191
|
+
cursor = 0
|
|
192
|
+
data_len = len(data)
|
|
193
|
+
|
|
194
|
+
while cursor <= data_len - min_length:
|
|
195
|
+
if data[cursor : cursor + header_len] != HIK_ENCRYPTION_HEADER:
|
|
196
|
+
next_header = data.find(HIK_ENCRYPTION_HEADER, cursor + 1)
|
|
197
|
+
if next_header == -1:
|
|
198
|
+
break
|
|
199
|
+
cursor = next_header
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
next_header = data.find(HIK_ENCRYPTION_HEADER, cursor + header_len)
|
|
203
|
+
block = data[cursor : next_header if next_header != -1 else data_len]
|
|
204
|
+
if len(block) < min_length:
|
|
205
|
+
break
|
|
206
|
+
blocks.append(block)
|
|
207
|
+
if next_header == -1:
|
|
208
|
+
break
|
|
209
|
+
cursor = next_header
|
|
210
|
+
|
|
211
|
+
return blocks
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _decrypt_single_block(
|
|
215
|
+
block: bytes, password: str, header_len: int, hash_end: int
|
|
216
|
+
) -> bytes:
|
|
217
|
+
"""Decrypt a single hikencodepicture block."""
|
|
218
|
+
file_hash = block[header_len:hash_end]
|
|
167
219
|
passwd_hash = md5(str.encode(md5(str.encode(password)).hexdigest())).hexdigest()
|
|
168
220
|
if file_hash != str.encode(passwd_hash):
|
|
169
221
|
raise PyEzvizError("Invalid password")
|
|
170
222
|
|
|
223
|
+
ciphertext = block[hash_end:]
|
|
224
|
+
if not ciphertext:
|
|
225
|
+
raise PyEzvizError("Missing ciphertext payload")
|
|
226
|
+
|
|
227
|
+
remainder = len(ciphertext) % AES.block_size
|
|
228
|
+
if remainder:
|
|
229
|
+
_LOGGER.debug(
|
|
230
|
+
"Ciphertext not aligned to 16 bytes; trimming %s trailing bytes", remainder
|
|
231
|
+
)
|
|
232
|
+
ciphertext = ciphertext[:-remainder]
|
|
233
|
+
if not ciphertext:
|
|
234
|
+
raise PyEzvizError("Ciphertext too short after alignment adjustment")
|
|
235
|
+
|
|
171
236
|
key = str.encode(password.ljust(16, "\u0000")[:16])
|
|
172
237
|
iv_code = bytes([48, 49, 50, 51, 52, 53, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
173
238
|
cipher = AES.new(key, AES.MODE_CBC, iv_code)
|
|
174
239
|
|
|
175
|
-
next_chunk = b""
|
|
176
|
-
output_data = b""
|
|
177
|
-
finished = False
|
|
178
|
-
i = 48 # offset HIK header + hash
|
|
179
240
|
chunk_size = 1024 * AES.block_size
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
241
|
+
output_data = bytearray()
|
|
242
|
+
|
|
243
|
+
for start in range(0, len(ciphertext), chunk_size):
|
|
244
|
+
block_chunk = cipher.decrypt(ciphertext[start : start + chunk_size])
|
|
245
|
+
if start + chunk_size >= len(ciphertext):
|
|
246
|
+
padding_length = block_chunk[-1]
|
|
247
|
+
block_chunk = block_chunk[:-padding_length]
|
|
248
|
+
output_data.extend(block_chunk)
|
|
249
|
+
|
|
250
|
+
return bytes(output_data)
|
|
251
|
+
|
|
189
252
|
|
|
190
253
|
|
|
191
254
|
def return_password_hash(password: str) -> str:
|
|
@@ -12,10 +12,10 @@ pyezvizapi/models.py,sha256=NQzwTP0yEe2IWU-Vc6nAn87xulpTuo0MX2Rcf0WxifA,4176
|
|
|
12
12
|
pyezvizapi/mqtt.py,sha256=JGjO-uXdKtLidYN1wEZ_bxEKIlNLmnB82Ziiac6oxWs,22373
|
|
13
13
|
pyezvizapi/test_cam_rtsp.py,sha256=pbuanoKs_Pryt2f5QctHIngzJG1nD6kv8nulQYh2yPc,5162
|
|
14
14
|
pyezvizapi/test_mqtt.py,sha256=gBaurvo2bu-7sOe14AqNouepJHI-tPWzC3WTpDQbvHM,4155
|
|
15
|
-
pyezvizapi/utils.py,sha256=
|
|
16
|
-
pyezvizapi-1.0.4.
|
|
17
|
-
pyezvizapi-1.0.4.
|
|
18
|
-
pyezvizapi-1.0.4.
|
|
19
|
-
pyezvizapi-1.0.4.
|
|
20
|
-
pyezvizapi-1.0.4.
|
|
21
|
-
pyezvizapi-1.0.4.
|
|
15
|
+
pyezvizapi/utils.py,sha256=B-Lt4p14TTcJyrZvJ699GiUGj6qcd4vGmkKUf13bf_I,15455
|
|
16
|
+
pyezvizapi-1.0.4.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
+
pyezvizapi-1.0.4.2.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
18
|
+
pyezvizapi-1.0.4.2.dist-info/METADATA,sha256=_fdaKK2rdx9u4pHrjjkDHvBYZs5rTJqrh0OqkTioojg,7609
|
|
19
|
+
pyezvizapi-1.0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
pyezvizapi-1.0.4.2.dist-info/top_level.txt,sha256=gMZTelIi8z7pXyTCQLLaIkxVRrDQ_lS2NEv0WgfHrHs,11
|
|
21
|
+
pyezvizapi-1.0.4.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|