1
1
"""Handling Matter OTA provider."""
2
2
3
3
import asyncio
4
+ from base64 import b64encode
4
5
from dataclasses import asdict , dataclass
5
6
import functools
7
+ import hashlib
6
8
import json
7
9
import logging
8
10
from pathlib import Path
12
14
13
15
from aiohttp import ClientError , ClientSession
14
16
17
+ from matter_server .common .errors import UpdateError
15
18
from matter_server .common .helpers .util import dataclass_from_dict
16
19
17
20
if TYPE_CHECKING :
21
24
22
25
DEFAULT_UPDATES_PATH : Final [Path ] = Path ("updates" )
23
26
27
+ # From Matter SDK src/app/ota_image_tool.py
28
+ CHECHKSUM_TYPES : Final [dict [int , str ]] = {
29
+ 1 : "sha256" ,
30
+ 2 : "sha256_128" ,
31
+ 3 : "sha256_120" ,
32
+ 4 : "sha256_96" ,
33
+ 5 : "sha256_64" ,
34
+ 6 : "sha256_32" ,
35
+ 7 : "sha384" ,
36
+ 8 : "sha512" ,
37
+ 9 : "sha3_224" ,
38
+ 10 : "sha3_256" ,
39
+ 11 : "sha3_384" ,
40
+ 12 : "sha3_512" ,
41
+ }
42
+
24
43
25
44
@dataclass
26
45
class DeviceSoftwareVersionModel : # pylint: disable=C0103
@@ -231,11 +250,22 @@ async def download_update(self, update_desc: dict) -> None:
231
250
)
232
251
233
252
file_path = DEFAULT_UPDATES_PATH / file_name
234
- if await loop .run_in_executor (None , file_path .exists ):
235
- LOGGER .info ("File '%s' exists already, skipping download." , file_name )
236
- return
237
253
238
254
try :
255
+ checksum_alg = None
256
+ if (
257
+ "otaChecksum" in update_desc
258
+ and "otaChecksumType" in update_desc
259
+ and update_desc ["otaChecksumType" ] in CHECHKSUM_TYPES
260
+ ):
261
+ checksum_alg = hashlib .new (
262
+ CHECHKSUM_TYPES [update_desc ["otaChecksumType" ]]
263
+ )
264
+ else :
265
+ LOGGER .warning (
266
+ "No OTA checksum type or not supported, OTA will not be checked."
267
+ )
268
+
239
269
async with ClientSession (raise_for_status = True ) as session :
240
270
# fetch the paa certificates list
241
271
LOGGER .debug ("Download update from '%s'." , url )
@@ -246,8 +276,21 @@ async def download_update(self, update_desc: dict) -> None:
246
276
if not chunk :
247
277
break
248
278
await loop .run_in_executor (None , f .write , chunk )
249
-
250
- # TODO: Check against otaChecksum
279
+ if checksum_alg :
280
+ checksum_alg .update (chunk )
281
+
282
+ # Download finished, check checksum if necessary
283
+ if checksum_alg :
284
+ checksum = b64encode (checksum_alg .digest ()).decode ("ascii" )
285
+ if checksum != update_desc ["otaChecksum" ]:
286
+ LOGGER .error (
287
+ "Checksum mismatch for file '%s', expected: %s, got: %s" ,
288
+ file_name ,
289
+ update_desc ["otaChecksum" ],
290
+ checksum ,
291
+ )
292
+ await loop .run_in_executor (None , file_path .unlink )
293
+ raise UpdateError ("Checksum mismatch!" )
251
294
252
295
LOGGER .info (
253
296
"File '%s' downloaded to '%s'" , file_name , DEFAULT_UPDATES_PATH
0 commit comments