twintig: Be less verbose by default
[svpe-tools.git] / twintig.c
blobc77cf2bc0104aca18e687b64b3b7ea1b3df0f7ee
1 // Copyright 2007,2008 Segher Boessenkool <segher@kernel.crashing.org>
2 // Licensed under the terms of the GNU GPL, version 2
3 // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <dirent.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdio.h>
13 #include "tools.h"
15 static int verbose = 0;
17 #define MAXFILES 1000
19 #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0)
21 static u8 sd_key[16];
22 static u8 sd_iv[16];
23 static u8 md5_blanker[16];
25 static u32 ng_id;
26 static u32 ng_key_id;
27 static u8 ng_mac[6];
28 static u8 ng_priv[30];
29 static u8 ng_sig[60];
31 static FILE *fp;
33 static u8 header[0xf0c0];
35 static u32 n_files;
36 static u32 files_size;
38 static u8 files[MAXFILES][0x80];
40 static void read_image(u8 *data, u32 w, u32 h, const char *name)
42 FILE *fp;
43 u32 x, y;
44 u32 ww, hh;
46 fp = fopen(name, "rb");
47 if (!fp)
48 fatal("open %s", name);
50 if (fscanf(fp, "P6 %d %d 255\n", &ww, &hh) != 2)
51 ERROR("bad ppm");
52 if (ww != w || hh != h)
53 ERROR("wrong size ppm");
55 for (y = 0; y < h; y++)
56 for (x = 0; x < w; x++) {
57 u8 pix[3];
58 u16 raw;
59 u32 x0, x1, y0, y1, off;
61 x0 = x & 3;
62 x1 = x >> 2;
63 y0 = y & 3;
64 y1 = y >> 2;
65 off = x0 + 4 * y0 + 16 * x1 + 4 * w * y1;
67 if (fread(pix, 3, 1, fp) != 1)
68 fatal("read %s", name);
70 raw = (pix[0] & 0xf8) << 7;
71 raw |= (pix[1] & 0xf8) << 2;
72 raw |= (pix[2] & 0xf8) >> 3;
73 raw |= 0x8000;
75 wbe16(data + 2*off, raw);
78 fclose(fp);
81 static u8 perm_from_path(const char *path)
83 struct stat sb;
84 mode_t mode;
85 u8 perm;
86 u32 i;
88 if (stat(path, &sb))
89 fatal("stat %s", path);
91 perm = 0;
92 mode = sb.st_mode;
93 for (i = 0; i < 3; i++) {
94 perm <<= 2;
95 if (mode & 0200)
96 perm |= 2;
97 if (mode & 0400)
98 perm |= 1;
99 mode <<= 3;
102 return perm;
105 static void do_file_header(u64 title_id)
107 u8 md5_calc[16];
108 FILE *in;
109 char name[256];
110 u32 i;
112 memset(header, 0, sizeof header);
114 wbe64(header, title_id);
115 header[0x0c] = perm_from_path(".");
116 memcpy(header + 0x0e, md5_blanker, 16);
117 memcpy(header + 0x20, "WIBN", 4);
118 // XXX: what about the stuff at 0x24?
120 in = fopen("###title###", "rb");
121 if (!in)
122 fatal("open ###title###");
123 if (fread(header + 0x40, 0x80, 1, in) != 1)
124 fatal("read ###title###");
125 fclose(in);
127 read_image(header + 0xc0, 192, 64, "###banner###.ppm");
129 in = fopen("###icon###.ppm", "rb");
130 if (in) {
131 fclose(in);
132 wbe32(header + 8, 0x72a0);
133 read_image(header + 0x60c0, 48, 48, "###icon###.ppm");
134 } else {
135 wbe32(header + 8, 0xf0a0);
137 for (i = 0; i < 8; i++) {
138 snprintf(name, sizeof name, "###icon%d###.ppm", i);
139 read_image(header + 0x60c0 + 0x1200*i, 48, 48, name);
143 md5(header, sizeof header, md5_calc);
144 memcpy(header + 0x0e, md5_calc, 16);
145 aes_cbc_enc(sd_key, sd_iv, header, sizeof header, header);
147 if (fwrite(header, 0xf0c0, 1, fp) != 1)
148 fatal("write header");
151 static void find_files_recursive(const char *path)
153 DIR *dir;
154 struct dirent *de;
155 char name[53];
156 u32 len;
157 int is_dir;
158 u8 *p;
159 struct stat sb;
160 u32 size;
162 dir = opendir(path ? path : ".");
163 if (!dir)
164 fatal("opendir %s", path ? path : ".");
166 while ((de = readdir(dir))) {
167 if (strcmp(de->d_name, ".") == 0)
168 continue;
169 if (strcmp(de->d_name, "..") == 0)
170 continue;
171 if (strncmp(de->d_name, "###", 3) == 0)
172 continue;
174 if (path == 0)
175 len = snprintf(name, sizeof name, "%s", de->d_name);
176 else
177 len = snprintf(name, sizeof name, "%s/%s", path,
178 de->d_name);
180 if (len >= sizeof name)
181 ERROR("path too long");
183 if (de->d_type != DT_REG && de->d_type != DT_DIR)
184 ERROR("not a regular file or a directory");
186 is_dir = (de->d_type == DT_DIR);
188 if (is_dir)
189 size = 0;
190 else {
191 if (stat(name, &sb))
192 fatal("stat %s", name);
193 size = sb.st_size;
196 p = files[n_files++];
197 wbe32(p, 0x3adf17e);
198 wbe32(p + 4, size);
199 p[8] = perm_from_path(name);
200 p[0x0a] = is_dir ? 2 : 1;
201 strcpy(p + 0x0b, name);
202 // maybe fill up with dirt
204 size = round_up(size, 0x40);
205 files_size += 0x80 + size;
207 if (de->d_type == DT_DIR)
208 find_files_recursive(name);
211 if (closedir(dir))
212 fatal("closedir");
215 static int compar(const void *a, const void *b)
217 return strcmp((char *)a + 0x0b, (char *)b + 0x0b);
220 static void find_files(void)
222 n_files = 0;
223 files_size = 0;
225 memset(files, 0, sizeof files);
227 find_files_recursive(0);
229 qsort(files, n_files, 0x80, compar);
232 static void do_backup_header(u64 title_id)
234 u8 header[0x80];
236 memset(header, 0, sizeof header);
238 wbe32(header, 0x70);
239 wbe32(header + 4, 0x426b0001);
240 wbe32(header + 8, ng_id);
241 wbe32(header + 0x0c, n_files);
242 wbe32(header + 0x10, files_size);
243 wbe32(header + 0x1c, files_size + 0x3c0);
245 wbe64(header + 0x60, title_id);
246 memcpy(header + 0x68, ng_mac, 6);
248 if (fwrite(header, sizeof header, 1, fp) != 1)
249 fatal("write Bk header");
252 static void do_file(u32 file_no)
254 u8 *header;
255 u32 size;
256 u32 rounded_size;
257 u8 perm, attr, type;
258 char *name;
259 u8 *data;
260 FILE *in;
262 header = files[file_no];
264 size = be32(header + 4);
265 perm = header[8];
266 attr = header[9];
267 type = header[10];
268 name = header + 11;
270 if (verbose)
271 printf(
272 "file: size=%08x perm=%02x attr=%02x type=%02x name=%s\n",
273 size, perm, attr, type, name);
275 if (fwrite(header, 0x80, 1, fp) != 1)
276 fatal("write file header %d", file_no);
278 if (type == 1) {
279 rounded_size = round_up(size, 0x40);
281 data = malloc(rounded_size);
282 if (!data)
283 fatal("malloc data");
285 in = fopen(name, "rb");
286 if (!in)
287 fatal("open %s", name);
288 if (fread(data, size, 1, in) != 1)
289 fatal("read %s", name);
290 fclose(in);
292 memset(data + size, 0, rounded_size - size);
294 aes_cbc_enc(sd_key, header + 0x50, data, rounded_size, data);
296 if (fwrite(data, rounded_size, 1, fp) != 1)
297 fatal("write file %d", file_no);
299 free(data);
303 static void make_ec_cert(u8 *cert, u8 *sig, char *signer, char *name, u8 *priv,
304 u32 key_id)
306 memset(cert, 0, 0x180);
307 wbe32(cert, 0x10002);
308 memcpy(cert + 4, sig, 60);
309 strcpy(cert + 0x80, signer);
310 wbe32(cert + 0xc0, 2);
311 strcpy(cert + 0xc4, name);
312 wbe32(cert + 0x104, key_id);
313 ec_priv_to_pub(priv, cert + 0x108);
316 static void do_sig(void)
318 u8 sig[0x40];
319 u8 ng_cert[0x180];
320 u8 ap_cert[0x180];
321 u8 hash[0x14];
322 u8 ap_priv[30];
323 u8 ap_sig[60];
324 char signer[64];
325 char name[64];
326 u8 *data;
327 u32 data_size;
329 sprintf(signer, "Root-CA00000001-MS00000002");
330 sprintf(name, "NG%08x", ng_id);
331 make_ec_cert(ng_cert, ng_sig, signer, name, ng_priv, ng_key_id);
333 memset(ap_priv, 0, sizeof ap_priv);
334 ap_priv[10] = 1;
336 memset(ap_sig, 81, sizeof ap_sig); // temp
338 sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", ng_id);
339 sprintf(name, "AP%08x%08x", 1, 2);
340 make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv, 0);
342 sha(ap_cert + 0x80, 0x100, hash);
343 generate_ecdsa(ap_sig, ap_sig + 30, ng_priv, hash);
344 make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv, 0);
346 data_size = files_size + 0x80;
348 data = malloc(data_size);
349 if (!data)
350 fatal("malloc");
351 fseek(fp, 0xf0c0, SEEK_SET);
352 if (fread(data, data_size, 1, fp) != 1)
353 fatal("read data for sig check");
354 sha(data, data_size, hash);
355 sha(hash, 20, hash);
356 free(data);
358 generate_ecdsa(sig, sig + 30, ap_priv, hash);
359 wbe32(sig + 60, 0x2f536969);
361 if (fwrite(sig, sizeof sig, 1, fp) != 1)
362 fatal("write sig");
363 if (fwrite(ng_cert, sizeof ng_cert, 1, fp) != 1)
364 fatal("write NG cert");
365 if (fwrite(ap_cert, sizeof ap_cert, 1, fp) != 1)
366 fatal("write AP cert");
369 int main(int argc, char **argv)
371 u64 title_id;
372 u8 tmp[4];
373 u32 i;
375 if (argc != 3) {
376 fprintf(stderr, "Usage: %s <srcdir> <data.bin>\n", argv[0]);
377 return 1;
380 get_key("sd-key", sd_key, 16);
381 get_key("sd-iv", sd_iv, 16);
382 get_key("md5-blanker", md5_blanker, 16);
384 get_key("default/NG-id", tmp, 4);
385 ng_id = be32(tmp);
386 get_key("default/NG-key-id", tmp, 4);
387 ng_key_id = be32(tmp);
388 get_key("default/NG-mac", ng_mac, 6);
389 get_key("default/NG-priv", ng_priv, 30);
390 get_key("default/NG-sig", ng_sig, 60);
392 if (sscanf(argv[1], "%016llx", &title_id) != 1)
393 ERROR("not a correct title id");
395 fp = fopen(argv[2], "wb+");
396 if (!fp)
397 fatal("open %s", argv[2]);
399 if (chdir(argv[1]))
400 fatal("chdir %s", argv[1]);
402 do_file_header(title_id);
404 find_files();
406 do_backup_header(title_id);
408 for (i = 0; i < n_files; i++)
409 do_file(i);
411 if (chdir(".."))
412 fatal("chdir ..");
414 do_sig();
416 fclose(fp);
418 return 0;