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