Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/unblob/handlers/archive/dlink/encrpted_img.py: 59%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

44 statements  

1import io 

2from pathlib import Path 

3from typing import Optional 

4 

5from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 

6from structlog import get_logger 

7 

8from unblob.file_utils import File, InvalidInputFormat 

9from unblob.models import ( 

10 Endian, 

11 Extractor, 

12 HandlerDoc, 

13 HandlerType, 

14 Reference, 

15 Regex, 

16 StructHandler, 

17 ValidChunk, 

18) 

19 

20logger = get_logger() 

21 

22C_DEFINITIONS = r""" 

23 typedef struct encrpted_img_header { 

24 char magic[12]; /* encrpted_img */ 

25 uint32 size; /* total size of file */ 

26 } dlink_header_t; 

27""" 

28 

29HEADER_LEN = 16 

30PEB_SIZE = 0x20000 

31UBI_HEAD = b"\x55\x42\x49\x23\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 

32UBI_HEAD_LEN = len(UBI_HEAD) 

33KEY = b"he9-4+M!)d6=m~we1,q2a3d1n&2*Z^%8" 

34IV = b"J%1iQl8$=lm-;8AE" 

35 

36 

37class EncrptedExtractor(Extractor): 

38 def extract(self, inpath: Path, outdir: Path): 

39 cipher = Cipher(algorithms.AES(KEY), modes.CBC(IV)) 

40 decryptor = cipher.decryptor() 

41 outpath = outdir.joinpath(f"{inpath.name}.decrypted") 

42 outfile = outpath.open("wb") 

43 with inpath.open("rb") as f: 

44 f.seek(HEADER_LEN, io.SEEK_SET) # skip header 

45 ciphertext = f.read(PEB_SIZE) 

46 while ciphertext and len(ciphertext) % 16 == 0: 

47 plaintext = decryptor.update(ciphertext) 

48 outfile.write(UBI_HEAD + plaintext[UBI_HEAD_LEN:]) 

49 ciphertext = f.read(PEB_SIZE) 

50 outfile.write(decryptor.finalize()) 

51 outfile.close() 

52 

53 

54class EncrptedHandler(StructHandler): 

55 NAME = "encrpted_img" 

56 

57 PATTERNS = [ 

58 Regex(r"encrpted_img"), 

59 ] 

60 C_DEFINITIONS = C_DEFINITIONS 

61 HEADER_STRUCT = "dlink_header_t" 

62 EXTRACTOR = EncrptedExtractor() 

63 

64 DOC = HandlerDoc( 

65 name="D-Link encrpted_img", 

66 description="A binary format used by D-Link to store encrypted firmware or data. It consists of a custom 12-byte magic header followed by the encrypted payload.", 

67 handler_type=HandlerType.ARCHIVE, 

68 vendor="D-Link", 

69 references=[ 

70 Reference( 

71 title="How-To: Extracting Decryption Keys for D-Link", 

72 url="https://www.onekey.com/resource/extracting-decryption-keys-dlink", # Replace with actual reference if available 

73 ) 

74 ], 

75 limitations=[], 

76 ) 

77 

78 def is_valid_header(self, header) -> bool: 

79 return header.size >= len(header) 

80 

81 def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]: 

82 header = self.parse_header(file, endian=Endian.BIG) 

83 

84 if not self.is_valid_header(header): 

85 raise InvalidInputFormat("Invalid header length") 

86 

87 return ValidChunk( 

88 start_offset=start_offset, 

89 end_offset=start_offset + len(header) + header.size, 

90 )