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