293d753dc83007965114abee4c847e511555d4c3
[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 <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37  
38 #define DES_KEY "H@L9K*(3"
39  
40 #ifndef min
41 #define min(a,b) \
42    ({ __typeof__ (a) _a = (a); \
43        __typeof__ (b) _b = (b); \
44      _a < _b ? _a : _b; })
45 #endif
46  
47 #define IH_MAGIC    0x27051956
48 #define IH_NMLEN    32
49 typedef struct image_header {
50     uint32_t    ih_magic;   /* Image Header Magic Number    */
51     uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */
52     uint32_t    ih_time;    /* Image Creation Timestamp */
53     uint32_t    ih_size;    /* Image Data Size      */
54     uint32_t    ih_load;    /* Data  Load  Address      */
55     uint32_t    ih_ep;      /* Entry Point Address      */
56     uint32_t    ih_dcrc;    /* Image Data CRC Checksum  */
57     uint8_t     ih_os;      /* Operating System     */
58     uint8_t     ih_arch;    /* CPU architecture     */
59     uint8_t     ih_type;    /* Image Type           */
60     uint8_t     ih_comp;    /* Compression Type     */
61     uint8_t     ih_name[IH_NMLEN];  /* Image Name       */
62 } image_header_t;
63  
64 static int temp_fd = -1;
65 static DES_key_schedule schedule;
66  
67 static void show_usage(const char *arg0);
68 static void exit_cleanup(void);
69 static void copy_file(int src, int dst);
70 static void encrypt(void *p, off_t len);
71 static void decrypt(void *p, off_t len);
72  
73  
74 int main(int argc, char **argv)
75 {
76         int encrypt_opt = 0;
77         int decrypt_opt = 0;
78         int input_opt = 0;
79         int output_opt = 0;
80         char *input_filename = NULL;
81         char *output_filename = NULL;
82  
83         int input_fd;
84         int output_fd;
85         off_t file_len;
86         char *p;
87         char buf[sizeof(image_header_t) + 3];
88         image_header_t *header;
89  
90         while (1) {
91                 static struct option long_options[] = {
92                         {"encrypt", no_argument,       0, 'e'},
93                         {"decrypt", no_argument,       0, 'd'},
94                         {"input",   required_argument, 0, 'i'},
95                         {"output",  required_argument, 0, 'o'},
96                         {0,         0,                 0, 0  }
97                 };
98                 int option_index = 0;
99                 int c = getopt_long(argc, argv, "dei:o:",
100                                 long_options, &option_index);
101                 if (c == -1)
102                         break;
103  
104                 switch (c) {
105                 case 'd':
106                         decrypt_opt++;
107                         if (decrypt_opt > 1) {
108                                 fprintf(stderr, "%s: decrypt may only be specified once\n",
109                                         argv[0]);
110                                 show_usage(argv[0]);
111                         }
112                         break;
113  
114                 case 'e':
115                         encrypt_opt++;
116                         if (encrypt_opt > 1) {
117                                 fprintf(stderr, "%s: encrypt may only be specified once\n",
118                                         argv[0]);
119                                 show_usage(argv[0]);
120                         }
121                         break;
122  
123                 case 'i':
124                         input_opt++;
125                         if (input_opt > 1) {
126                                 fprintf(stderr, "%s: only one input file may be specified\n",
127                                         argv[0]);
128                                 show_usage(argv[0]);
129                         }
130                         if (strcmp("-", optarg) != 0) {
131                                 input_filename = optarg;
132                         }
133                         break;
134  
135                 case 'o':
136                         output_opt++;
137                         if (output_opt > 1) {
138                                 fprintf(stderr, "%s: only one output file may be specified\n",
139                                         argv[0]);
140                                 show_usage(argv[0]);
141                         }
142                         if (strcmp("-", optarg) != 0) {
143                                 output_filename = optarg;
144                         }
145                         break;
146  
147                 case '?':
148                         exit(-1);
149  
150                 default:
151                         abort();
152                 }
153         }
154  
155         if (decrypt_opt && encrypt_opt) {
156                 fprintf(stderr, "%s: decrypt and encrypt may not be used together\n",
157                         argv[0]);
158                 show_usage(argv[0]);
159         }
160  
161         if (!decrypt_opt && !encrypt_opt) {
162                 fprintf(stderr, "%s: neither decrypt or encrypt were specified\n",
163                         argv[0]);
164                 show_usage(argv[0]);
165         }
166  
167         temp_fd = fileno(tmpfile());
168         if (temp_fd < 0) {
169                 fprintf(stderr, "Can't create temporary file\n");
170                 exit(EXIT_FAILURE);
171         }
172  
173         atexit(exit_cleanup);
174         DES_set_key_unchecked((const_DES_cblock *)DES_KEY, &schedule);
175  
176         if (input_filename) {
177                 input_fd = open(input_filename, O_RDONLY);
178                 if (input_fd < 0) {
179                         fprintf(stderr, "Can't open %s for reading: %s\n", input_filename,
180                                 strerror(errno));
181                         exit(EXIT_FAILURE);
182                 }
183                 copy_file(input_fd, temp_fd);
184                 close(input_fd);
185         }
186         else {
187                 copy_file(STDIN_FILENO, temp_fd);
188         }
189  
190         file_len = lseek(temp_fd, 0, SEEK_CUR);
191         if (file_len < 64) {
192                 fprintf(stderr, "Not enough data\n");
193                 exit(EXIT_FAILURE);
194         }
195  
196         p = mmap(0, file_len, PROT_READ|PROT_WRITE, MAP_SHARED, temp_fd, 0);
197         if (p == MAP_FAILED) {
198                 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
199                 exit(EXIT_FAILURE);
200         }       
201  
202         if (encrypt_opt) {
203                 header = (image_header_t *)p;
204                 off_t len = min(file_len,
205                                 ntohl(header->ih_size) + sizeof(image_header_t));
206                 if (ntohl(header->ih_magic) != IH_MAGIC) {
207                         fprintf(stderr, "Header magic incorrect: "
208                                 "expected 0x%08X, got 0x%08X\n",
209                                 IH_MAGIC, ntohl(header->ih_magic));
210                         munmap(p, file_len);
211                         exit(EXIT_FAILURE);
212                 }
213                 encrypt(p, len);
214                 munmap(p, file_len);
215                 if (len != file_len) {
216                         if (ftruncate(temp_fd, len) < 0) {
217                                 fprintf(stderr, "ftruncate failed: %s\n", strerror(errno));
218                                 exit(EXIT_FAILURE);
219                         }
220                 }               
221         }
222  
223         if (decrypt_opt) {
224                 off_t header_len = min(file_len, sizeof(image_header_t) + 3);
225                 memcpy(buf, p, header_len);
226                 decrypt(buf, header_len);
227                 header = (image_header_t *)buf;
228                 if (ntohl(header->ih_magic) != IH_MAGIC) {
229                         fprintf(stderr, "Header magic incorrect: "
230                                 "expected 0x%08X, got 0x%08X\n",
231                                 IH_MAGIC, ntohl(header->ih_magic));
232                         exit(EXIT_FAILURE);
233                 }
234                 decrypt(p, file_len);
235                 munmap(p, file_len);
236         }
237  
238         lseek(temp_fd, 0, SEEK_SET);
239         if (output_filename) {
240                 output_fd = creat(output_filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
241                 if (output_fd < 0) {
242                         fprintf(stderr, "Can't open %s for writing: %s\n",
243                                 output_filename, strerror(errno));
244                         exit(EXIT_FAILURE);
245                 }
246                 copy_file(temp_fd, output_fd);
247                 close(output_fd);
248         }
249         else {
250                 copy_file(temp_fd, STDOUT_FILENO);
251         }
252  
253         exit(EXIT_SUCCESS);
254         return 0;
255 }
256  
257 static void show_usage(const char *arg0)
258 {
259         fprintf(stderr, "usage: %s -d|-e [-i FILE] [-o FILE]\n\n", arg0);
260         fprintf(stderr, "%-15s %s\n", "-d, --decrypt", "decrypt data");
261         fprintf(stderr, "%-15s %s\n", "-e, --encrypt", "encrypt data");
262         fprintf(stderr, "%-15s %s\n", "-i, --input", "intput file (defaults to stdin)");
263         fprintf(stderr, "%-15s %s\n", "-o, --output", "output file (defaults to stdout)");
264         exit(-1);
265 }
266  
267 static void exit_cleanup(void)
268 {
269         if (temp_fd >= 0) {
270                 close(temp_fd);
271         }
272 }
273  
274 static void copy_file(int src, int dst)
275 {
276         char buf[4096];
277         ssize_t size;
278  
279         while ((size = read(src, buf, 4096)) > 0) {
280         write(dst, buf, size);
281     }
282 }
283  
284 static void encrypt(void *p, off_t len)
285 {
286         DES_cblock *pblock;
287         int num_blocks;
288  
289         num_blocks = len / 8;
290         pblock = (DES_cblock *) p;
291         while (num_blocks--) {
292                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_ENCRYPT);
293                 pblock++;
294         }
295  
296         num_blocks = (len - 3) / 8;
297         pblock = (DES_cblock *) (p + 3);
298         while (num_blocks--) {
299                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_ENCRYPT);
300                 pblock++;
301         }
302 }
303  
304 static void decrypt(void *p, off_t len)
305 {
306         DES_cblock *pblock;
307         int num_blocks;
308  
309         num_blocks = (len - 3) / 8;
310         pblock = (DES_cblock *) (p + 3);
311         while (num_blocks--) {
312                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_DECRYPT);
313                 pblock++;
314         }
315  
316         num_blocks = len / 8;
317         pblock = (DES_cblock *) p;
318         while (num_blocks--) {
319                 DES_ecb_encrypt(pblock, pblock, &schedule, DES_DECRYPT);
320                 pblock++;
321         }
322 }