firmware-utils: add HLK-RM04 tool
[openwrt.git] / tools / firmware-utils / src / mkhilinkfw.c
1 /*
2  * Copyright (C) 2013 Jeff Kent <jeff@jkent.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  *
17  * This tool encrypts and decrypts uImage formatted firmware for Hilink
18  * HLK-RM04 wireless modules.  It will also truncate a dump of mtd6 and make
19  * it an image suitable for flashing via the stock firmware upgrade page.
20  *
21  * Build instructions: 
22  *   gcc -lcrypto hlkcrypt.c -o hlkcrypt
23  */
24  
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <getopt.h>
28 #include <openssl/des.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/mman.h>
34 #include <unistd.h>
35  
36 #define DES_KEY "H@L9K*(3"
37  
38 #ifndef min
39 #define min(a,b) \
40    ({ __typeof__ (a) _a = (a); \
41        __typeof__ (b) _b = (b); \
42      _a < _b ? _a : _b; })
43 #endif
44  
45 #define IH_MAGIC    0x27051956
46 #define IH_NMLEN    32
47 typedef struct image_header {
48     uint32_t    ih_magic;   /* Image Header Magic Number    */
49     uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */
50     uint32_t    ih_time;    /* Image Creation Timestamp */
51     uint32_t    ih_size;    /* Image Data Size      */
52     uint32_t    ih_load;    /* Data  Load  Address      */
53     uint32_t    ih_ep;      /* Entry Point Address      */
54     uint32_t    ih_dcrc;    /* Image Data CRC Checksum  */
55     uint8_t     ih_os;      /* Operating System     */
56     uint8_t     ih_arch;    /* CPU architecture     */
57     uint8_t     ih_type;    /* Image Type           */
58     uint8_t     ih_comp;    /* Compression Type     */
59     uint8_t     ih_name[IH_NMLEN];  /* Image Name       */
60 } image_header_t;
61  
62 static int temp_fd = -1;
63 static DES_key_schedule schedule;
64  
65 static void show_usage(const char *arg0);
66 static void exit_cleanup(void);
67 static void copy_file(int src, int dst);
68 static void encrypt(void *p, off_t len);
69 static void decrypt(void *p, off_t len);
70  
71  
72 int main(int argc, char **argv)
73 {
74         int encrypt_opt = 0;
75         int decrypt_opt = 0;
76         int input_opt = 0;
77         int output_opt = 0;
78         char *input_filename = NULL;
79         char *output_filename = NULL;
80  
81         int input_fd;
82         int output_fd;
83         off_t file_len;
84         char *p;
85         char buf[sizeof(image_header_t) + 3];
86         image_header_t *header;
87  
88         while (1) {
89                 static struct option long_options[] = {
90                         {"encrypt", no_argument,       0, 'e'},
91                         {"decrypt", no_argument,       0, 'd'},
92                         {"input",   required_argument, 0, 'i'},
93                         {"output",  required_argument, 0, 'o'},
94                         {0,         0,                 0, 0  }
95                 };
96                 int option_index = 0;
97                 int c = getopt_long(argc, argv, "dei:o:",
98                                 long_options, &option_index);
99                 if (c == -1)
100                         break;
101  
102                 switch (c) {
103                 case 'd':
104                         decrypt_opt++;
105                         if (decrypt_opt > 1) {
106                                 fprintf(stderr, "%s: decrypt may only be specified once\n",
107                                         argv[0]);
108                                 show_usage(argv[0]);
109                         }
110                         break;
111  
112                 case 'e':
113                         encrypt_opt++;
114                         if (encrypt_opt > 1) {
115                                 fprintf(stderr, "%s: encrypt may only be specified once\n",
116                                         argv[0]);
117                                 show_usage(argv[0]);
118                         }
119                         break;
120  
121                 case 'i':
122                         input_opt++;
123                         if (input_opt > 1) {
124                                 fprintf(stderr, "%s: only one input file may be specified\n",
125                                         argv[0]);
126                                 show_usage(argv[0]);
127                         }
128                         if (strcmp("-", optarg) != 0) {
129                                 input_filename = optarg;
130                         }
131                         break;
132  
133                 case 'o':
134                         output_opt++;
135                         if (output_opt > 1) {
136                                 fprintf(stderr, "%s: only one output file may be specified\n",
137                                         argv[0]);
138                                 show_usage(argv[0]);
139                         }
140                         if (strcmp("-", optarg) != 0) {
141                                 output_filename = optarg;
142                         }
143                         break;
144  
145                 case '?':
146                         exit(-1);
147  
148                 default:
149                         abort();
150                 }
151         }
152  
153         if (decrypt_opt && encrypt_opt) {
154                 fprintf(stderr, "%s: decrypt and encrypt may not be used together\n",
155                         argv[0]);
156                 show_usage(argv[0]);
157         }
158  
159         if (!decrypt_opt && !encrypt_opt) {
160                 fprintf(stderr, "%s: neither decrypt or encrypt were specified\n",
161                         argv[0]);
162                 show_usage(argv[0]);
163         }
164  
165         temp_fd = fileno(tmpfile());
166         if (temp_fd < 0) {
167                 fprintf(stderr, "Can't create temporary file\n");
168                 exit(EXIT_FAILURE);
169         }
170  
171         atexit(exit_cleanup);
172         DES_set_key_unchecked((const_DES_cblock *)DES_KEY, &schedule);
173  
174         if (input_filename) {
175                 input_fd = open(input_filename, O_RDONLY);
176                 if (input_fd < 0) {
177                         fprintf(stderr, "Can't open %s for reading: %s\n", input_filename,
178                                 strerror(errno));
179                         exit(EXIT_FAILURE);
180                 }
181                 copy_file(input_fd, temp_fd);
182                 close(input_fd);
183         }
184         else {
185                 copy_file(STDIN_FILENO, temp_fd);
186         }
187  
188         file_len = lseek(temp_fd, 0, SEEK_CUR);
189         if (file_len < 64) {
190                 fprintf(stderr, "Not enough data\n");
191                 exit(EXIT_FAILURE);
192         }
193  
194         p = mmap(0, file_len, PROT_READ|PROT_WRITE, MAP_SHARED, temp_fd, 0);
195         if (p == MAP_FAILED) {
196                 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
197                 exit(EXIT_FAILURE);
198         }       
199  
200         if (encrypt_opt) {
201                 header = (image_header_t *)p;
202                 off_t len = min(file_len,
203                                 ntohl(header->ih_size) + sizeof(image_header_t));
204                 if (ntohl(header->ih_magic) != IH_MAGIC) {
205                         fprintf(stderr, "Header magic incorrect: "
206                                 "expected 0x%08X, got 0x%08X\n",
207                                 IH_MAGIC, ntohl(header->ih_magic));
208                         munmap(p, file_len);
209                         exit(EXIT_FAILURE);
210                 }
211                 encrypt(p, len);
212                 munmap(p, file_len);
213                 if (len != file_len) {
214                         if (ftruncate(temp_fd, len) < 0) {
215                                 fprintf(stderr, "ftruncate failed: %s\n", strerror(errno));
216                                 exit(EXIT_FAILURE);
217                         }
218                 }               
219         }
220  
221         if (decrypt_opt) {
222                 off_t header_len = min(file_len, sizeof(image_header_t) + 3);
223                 memcpy(buf, p, header_len);
224                 decrypt(buf, header_len);
225                 header = (image_header_t *)buf;
226                 if (ntohl(header->ih_magic) != IH_MAGIC) {
227                         fprintf(stderr, "Header magic incorrect: "
228                                 "expected 0x%08X, got 0x%08X\n",
229                                 IH_MAGIC, ntohl(header->ih_magic));
230                         exit(EXIT_FAILURE);
231                 }
232                 decrypt(p, file_len);
233                 munmap(p, file_len);
234         }
235  
236         lseek(temp_fd, 0, SEEK_SET);
237         if (output_filename) {
238                 output_fd = creat(output_filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
239                 if (output_fd < 0) {
240                         fprintf(stderr, "Can't open %s for writing: %s\n",
241                                 output_filename, strerror(errno));
242                         exit(EXIT_FAILURE);
243                 }
244                 copy_file(temp_fd, output_fd);
245                 close(output_fd);
246         }
247         else {
248                 copy_file(temp_fd, STDOUT_FILENO);
249         }
250  
251         exit(EXIT_SUCCESS);
252         return 0;
253 }
254  
255 static void show_usage(const char *arg0)
256 {
257         fprintf(stderr, "usage: %s -d|-e [-i FILE] [-o FILE]\n\n", arg0);
258         fprintf(stderr, "%-15s %s\n", "-d, --decrypt", "decrypt data");
259         fprintf(stderr, "%-15s %s\n", "-e, --encrypt", "encrypt data");
260         fprintf(stderr, "%-15s %s\n", "-i, --input", "intput file (defaults to stdin)");
261         fprintf(stderr, "%-15s %s\n", "-o, --output", "output file (defaults to stdout)");
262         exit(-1);
263 }
264  
265 static void exit_cleanup(void)
266 {
267         if (temp_fd >= 0) {
268                 close(temp_fd);
269         }
270 }
271  
272 static void copy_file(int src, int dst)
273 {
274         char buf[4096];
275         ssize_t size;
276  
277         while ((size = read(src, buf, 4096)) > 0) {
278         write(dst, buf, size);
279     }
280 }
281  
282 static void encrypt(void *p, off_t len)
283 {
284         DES_cblock *pblock;
285         int num_blocks;
286  
287         num_blocks = len / 8;
288         pblock = (DES_cblock *) p;
289         while (num_blocks--) {
290                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_ENCRYPT);
291                 pblock++;
292         }
293  
294         num_blocks = (len - 3) / 8;
295         pblock = (DES_cblock *) (p + 3);
296         while (num_blocks--) {
297                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_ENCRYPT);
298                 pblock++;
299         }
300 }
301  
302 static void decrypt(void *p, off_t len)
303 {
304         DES_cblock *pblock;
305         int num_blocks;
306  
307         num_blocks = (len - 3) / 8;
308         pblock = (DES_cblock *) (p + 3);
309         while (num_blocks--) {
310                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_DECRYPT);
311                 pblock++;
312         }
313  
314         num_blocks = len / 8;
315         pblock = (DES_cblock *) p;
316         while (num_blocks--) {
317                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_DECRYPT);
318                 pblock++;
319         }
320 }