1212import logging
1313import struct
1414import time
15- from cryptography .hazmat .primitives .ciphers import Cipher
16- from cryptography .hazmat .decrepit .ciphers .algorithms import ARC4
1715from crccheck import crc
1816from dataclasses import dataclass
1917
2018logger = logging .getLogger ("snagrecover" )
2119from snagrecover .protocols import rockchip
20+ from snagrecover .protocols import dfu
2221from snagrecover .utils import BinFileHeader
2322from snagrecover .config import recovery_config
2423
24+
2525# List generated with a grep on rkbin repository
26- NEWIDB_LIST = [ "rk3506" , "rk3506b" , "rk3528" , "rk3562" , "rk3566" , "rk3568" , "rk3576" , "rk3583" , "rk3588" , "rv1103b" , "rv1106" ]
26+ NEWIDB_LIST = ["rk3506" , "rk3506b" , "rk3528" , "rk3562" , "rk3566" , "rk3568" , "rk3576" , "rk3583" , "rk3588" , "rv1103b" , "rv1106" ]
2727
2828BOOTTAG = b"BOOT"
2929LDRTAG = b"LDR "
30- TAG_LIST = [ BOOTTAG , LDRTAG ]
30+ TAG_LIST = [BOOTTAG , LDRTAG ]
3131BOOTENTRYSIZE = 57
3232BOOTHEADERENTRYSIZE = 6
3333BOOTHEADERSIZE = 102
3434BOOTHEADERTIMESIZE = 7
3535RC4_KEY = bytearray ([124 , 78 , 3 , 4 , 85 , 5 , 9 , 7 , 45 , 44 , 123 , 56 , 23 , 13 , 23 , 17 ])
3636
37+
3738@dataclass
3839class BootEntry (BinFileHeader ):
3940 size : int
@@ -76,6 +77,7 @@ class BootReleaseTime(BinFileHeader):
7677 def __str__ (self ):
7778 return f"{ self .year } /{ self .month } /{ self .day } { self .hour } :{ self .minute } :{ self .second } "
7879
80+
7981class LoaderFileError (Exception ):
8082 def __init__ (self , message ):
8183 self .message = message
@@ -84,6 +86,7 @@ def __init__(self, message):
8486 def __str__ (self ):
8587 return f"File format error: { self .message } "
8688
89+
8790@dataclass
8891class BootHeader (BinFileHeader ):
8992 tag : bytes
@@ -105,7 +108,7 @@ class BootHeader(BinFileHeader):
105108
106109 def __post_init__ (self ):
107110 if self .tag not in TAG_LIST :
108- raise LoaderFileError (f"Invalid tag { self .header . tag } " )
111+ raise LoaderFileError (f"Invalid tag { self .tag } " )
109112 # not sure how to exactly parse version/merge_version
110113 self .maj_ver = self .version >> 8
111114 self .min_ver = self .version & 0xff
@@ -128,6 +131,7 @@ def __post_init__(self):
128131 def __str__ (self ):
129132 return f"{ self .tag } , { self .size } ,{ self .maj_ver } .{ self .min_ver } , 0x{ self .merge_version :0x} , { self .releasetime } , { self .chip } , { self .entry471 } , { self .entry472 } , { self .loader } , sign: { self .sign } , enc: { self .rc4 } "
130133
134+
131135class RkCrc32 (crc .Crc32Base ):
132136 """CRC-32/ROCKCHIP
133137 """
@@ -139,6 +143,7 @@ class RkCrc32(crc.Crc32Base):
139143 _reflect_output = False
140144 _xor_output = 0
141145
146+
142147class LoaderFile ():
143148 def __init__ (self , blob ):
144149 self .blob = blob
@@ -170,7 +175,7 @@ def __init__(self, blob):
170175 (self .crc32 ,) = struct .unpack ("<I" , crc32 )
171176 assert self .crc32 == calc_crc32
172177
173- def entry_data (self , name , idx = 0 ):
178+ def entry_data (self , name , idx = 0 ):
174179 entry = None
175180 if name == "471" :
176181 entry = self .entry471
@@ -190,6 +195,38 @@ def entry_data(self, name, idx = 0):
190195 def __str__ (self ):
191196 return f"{ self .header } crc: { self .crc32 :02x} "
192197
198+
199+ # Manual implementation of RC4, from Wikipedia's page
200+ # Not very secure, so don't use it elsewhere.
201+ class rc4 ():
202+ def __init__ (self ):
203+ self .S = list (range (256 ))
204+ self .i = 0
205+ self .j = 0
206+
207+ def ksa (self , key ):
208+ keylength = len (key )
209+ self .S = list (range (256 ))
210+ j = 0
211+ for i in range (256 ):
212+ j = (j + self .S [i ] + key [i % keylength ]) % 256
213+ self .S [i ], self .S [j ] = self .S [j ], self .S [i ]
214+
215+ def prga (self ):
216+ self .i = (self .i + 1 ) % 256
217+ self .j = (self .j + self .S [self .i ]) % 256
218+ self .S [self .i ], self .S [self .j ] = self .S [self .j ], self .S [self .i ]
219+ K = self .S [(self .S [self .i ] + self .S [self .j ]) % 256 ]
220+ return K
221+
222+ def encrypt (self , buf ):
223+ obuf = bytearray (len (buf ))
224+
225+ for offset in list (range (len (buf ))):
226+ obuf [offset ] = buf [offset ] ^ self .prga ()
227+ return obuf
228+
229+
193230def rc4_encrypt (fw_blob ):
194231
195232 soc_model = recovery_config ["soc_model" ]
@@ -201,28 +238,39 @@ def rc4_encrypt(fw_blob):
201238 padded_len = (blob_len + 4095 )// 4096 * 4096
202239 fw_blob = bytearray (fw_blob )
203240 fw_blob += bytearray ([0 ]* (padded_len - blob_len ))
204- a = ARC4 (RC4_KEY )
205- c = Cipher (a , mode = None )
206- d = c .encryptor ()
241+ encoder = rc4 ()
242+ encoder .ksa (RC4_KEY )
207243 obuf = bytearray ()
208244 for i in range (padded_len ):
209- obuf += d . update (fw_blob [i * 512 :(i + 1 )* 512 ])
245+ obuf += encoder . encrypt (fw_blob [i * 512 :(i + 1 )* 512 ])
210246 return obuf
211247
248+
212249def rockchip_run (dev , fw_name , fw_blob ):
213- rom = rockchip .RochipBootRom (dev )
214250
215251 if fw_name == 'code471' :
216252 logger .info ("Downloading code471..." )
253+ rom = rockchip .RochipBootRom (dev )
217254 blob = rc4_encrypt (fw_blob )
218255 rom .write_blob (blob , 0x471 )
219256 elif fw_name == 'code472' :
220257 logger .info ("Downloading code472..." )
258+ rom = rockchip .RochipBootRom (dev )
221259 blob = rc4_encrypt (fw_blob )
222260 rom .write_blob (blob , 0x472 )
261+ elif fw_name == "u-boot-fit" :
262+ id = dfu .search_partid (dev , "u-boot.itb" )
263+ if id is None :
264+ logger .error ("Missing u-boot.itb DFU partition" )
265+ dfu_cmd = dfu .DFU (dev , stm32 = False )
266+ dfu_cmd .get_status ()
267+ dfu_cmd .download_and_run (fw_blob , id , 0 , len (fw_blob ))
268+ dfu_cmd .get_status ()
269+ dfu_cmd .detach (id )
223270 else :
224271 fw = LoaderFile (fw_blob )
225272 logger .info (f"{ fw } " )
273+ rom = rockchip .RochipBootRom (dev )
226274 for i in range (fw .header .entry471 .count ):
227275 logger .info (f"Downloading entry 471 { i } ..." )
228276 (data , delay ) = fw .entry_data ("471" , i )
0 commit comments