"Whoops".
[svpe-tools.git] / twintig.c
blob963a8ed6dc3d121f98f710815cfd41ef85cfc0bb
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 #define MAXFILES 1000
17 #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0)
19 static u8 sd_key[16];
20 static u8 sd_iv[16];
21 static u8 md5_blanker[16];
23 static u32 ng_id;
24 static u8 ng_mac[6];
25 static u8 ng_priv[30];
26 static u8 ng_sig[60];
28 static FILE *fp;
30 static u8 header[0xf0c0];
32 static u32 n_files;
33 static u32 files_size;
35 static u8 files[MAXFILES][0x80];
37 static void read_image(u8 *data, u32 w, u32 h, const char *name)
39 FILE *fp;
40 u32 x, y;
41 u32 ww, hh;
43 fp = fopen(name, "rb");
44 if (!fp)
45 fatal("open %s", name);
47 if (fscanf(fp, "P6 %d %d 255\n", &ww, &hh) != 2)
48 ERROR("bad ppm");
49 if (ww != w || hh != h)
50 ERROR("wrong size ppm");
52 for (y = 0; y < h; y++)
53 for (x = 0; x < w; x++) {
54 u8 pix[3];
55 u16 raw;
56 u32 x0, x1, y0, y1, off;
58 x0 = x & 3;
59 x1 = x >> 2;
60 y0 = y & 3;
61 y1 = y >> 2;
62 off = x0 + 4 * y0 + 16 * x1 + 4 * w * y1;
64 if (fread(pix, 3, 1, fp) != 1)
65 fatal("read %s", name);
67 raw = (pix[0] & 0xf8) << 7;
68 raw |= (pix[1] & 0xf8) << 2;
69 raw |= (pix[2] & 0xf8) >> 3;
70 raw |= 0x8000;
72 wbe16(data + 2*off, raw);
75 fclose(fp);
78 static u8 perm_from_path(const char *path)
80 struct stat sb;
81 mode_t mode;
82 u8 perm;
83 u32 i;
85 if (stat(path, &sb))
86 fatal("stat %s", path);
88 perm = 0;
89 mode = sb.st_mode;
90 for (i = 0; i < 3; i++) {
91 perm <<= 2;
92 if (mode & 0200)
93 perm |= 2;
94 if (mode & 0400)
95 perm |= 1;
96 mode <<= 3;
99 return perm;
102 static void do_file_header(u64 title_id)
104 u8 md5_calc[16];
105 FILE *in;
106 char name[256];
107 u32 i;
109 memset(header, 0, sizeof header);
111 wbe64(header, title_id);
112 header[0x0c] = perm_from_path(".");
113 memcpy(header + 0x0e, md5_blanker, 16);
114 memcpy(header + 0x20, "WIBN", 4);
115 // XXX: what about the stuff at 0x24?
117 in = fopen("###title###", "rb");
118 if (!in)
119 fatal("open ###title###");
120 if (fread(header + 0x40, 0x80, 1, in) != 1)
121 fatal("read ###title###");
122 fclose(in);
124 read_image(header + 0xc0, 192, 64, "###banner###.ppm");
126 in = fopen("###icon###.ppm", "rb");
127 if (in) {
128 fclose(in);
129 wbe32(header + 8, 0x72a0);
130 read_image(header + 0x60c0, 48, 48, "###icon###.ppm");
131 } else {
132 wbe32(header + 8, 0xf0a0);
134 for (i = 0; i < 8; i++) {
135 snprintf(name, sizeof name, "###icon%d###.ppm", i);
136 read_image(header + 0x60c0 + 0x1200*i, 48, 48, name);
140 md5(header, sizeof header, md5_calc);
141 memcpy(header + 0x0e, md5_calc, 16);
142 aes_cbc_enc(sd_key, sd_iv, header, sizeof header, header);
144 if (fwrite(header, 0xf0c0, 1, fp) != 1)
145 fatal("write header");
148 static void find_files_recursive(const char *path)
150 DIR *dir;
151 struct dirent *de;
152 char name[53];
153 u32 len;
154 int is_dir;
155 u8 *p;
156 struct stat sb;
157 u32 size;
159 dir = opendir(path ? path : ".");
160 if (!dir)
161 fatal("opendir %s", path ? path : ".");
163 while ((de = readdir(dir))) {
164 if (strcmp(de->d_name, ".") == 0)
165 continue;
166 if (strcmp(de->d_name, "..") == 0)
167 continue;
168 if (strncmp(de->d_name, "###", 3) == 0)
169 continue;
171 if (path == 0)
172 len = snprintf(name, sizeof name, "%s", de->d_name);
173 else
174 len = snprintf(name, sizeof name, "%s/%s", path,
175 de->d_name);
177 if (len >= sizeof name)
178 ERROR("path too long");
180 if (de->d_type != DT_REG && de->d_type != DT_DIR)
181 ERROR("not a regular file or a directory");
183 is_dir = (de->d_type == DT_DIR);
185 if (is_dir)
186 size = 0;
187 else {
188 if (stat(name, &sb))
189 fatal("stat %s", name);
190 size = sb.st_size;
193 p = files[n_files++];
194 wbe32(p, 0x3adf17e);
195 wbe32(p + 4, size);
196 p[8] = perm_from_path(name);
197 p[0x0a] = is_dir ? 2 : 1;
198 strcpy(p + 0x0b, name);
199 // maybe fill up with dirt
201 size = round_up(size, 0x40);
202 files_size += 0x80 + size;
204 if (de->d_type == DT_DIR)
205 find_files_recursive(name);
208 if (closedir(dir))
209 fatal("closedir");
212 static int compar(const void *a, const void *b)
214 return strcmp((char *)a + 0x0b, (char *)b + 0x0b);
217 static void find_files(void)
219 n_files = 0;
220 files_size = 0;
222 memset(files, 0, sizeof files);
224 find_files_recursive(0);
226 qsort(files, n_files, 0x80, compar);
229 static void do_backup_header(u64 title_id)
231 u8 header[0x80];
233 memset(header, 0, sizeof header);
235 wbe32(header, 0x70);
236 wbe32(header + 4, 0x426b0001);
237 wbe32(header + 8, ng_id);
238 wbe32(header + 0x0c, n_files);
239 wbe32(header + 0x10, files_size);
240 wbe32(header + 0x1c, files_size + 0x3c0);
242 wbe64(header + 0x60, title_id);
243 memcpy(header + 0x68, ng_mac, 6);
245 if (fwrite(header, sizeof header, 1, fp) != 1)
246 fatal("write Bk header");
249 static void do_file(u32 file_no)
251 u8 *header;
252 u32 size;
253 u32 rounded_size;
254 u8 perm, attr, type;
255 char *name;
256 u8 *data;
257 FILE *in;
259 header = files[file_no];
261 size = be32(header + 4);
262 perm = header[8];
263 attr = header[9];
264 type = header[10];
265 name = header + 11;
267 fprintf(stderr,
268 "file: size=%08x perm=%02x attr=%02x type=%02x name=%s\n",
269 size, perm, attr, type, name);
271 if (fwrite(header, 0x80, 1, fp) != 1)
272 fatal("write file header %d", file_no);
274 if (type == 1) {
275 rounded_size = round_up(size, 0x40);
277 data = malloc(rounded_size);
278 if (!data)
279 fatal("malloc data");
281 in = fopen(name, "rb");
282 if (!in)
283 fatal("open %s", name);
284 if (fread(data, size, 1, in) != 1)
285 fatal("read %s", name);
286 fclose(in);
288 memset(data + size, 0, rounded_size - size);
290 aes_cbc_enc(sd_key, header + 0x50, data, rounded_size, data);
292 if (fwrite(data, rounded_size, 1, fp) != 1)
293 fatal("write file %d", file_no);
295 free(data);
299 static void make_ec_cert(u8 *cert, u8 *sig, char *signer, char *name, u8 *priv)
301 memset(cert, 0, 0x180);
302 wbe32(cert, 0x10002);
303 memcpy(cert + 4, sig, 60);
304 strcpy(cert + 0x80, signer);
305 wbe32(cert + 0xc0, 2);
306 strcpy(cert + 0xc4, name);
307 wbe32(cert + 0x104, 0); // key id
308 ec_priv_to_pub(priv, cert + 0x108);
311 static void do_sig(void)
313 u8 sig[0x40];
314 u8 ng_cert[0x180];
315 u8 ap_cert[0x180];
316 u8 hash[0x14];
317 u8 ap_priv[30];
318 u8 ap_sig[60];
319 char signer[64];
320 char name[64];
321 u8 *data;
322 u32 data_size;
324 sprintf(signer, "Root-CA00000001-MS00000002");
325 sprintf(name, "NG%08x", ng_id);
326 make_ec_cert(ng_cert, ng_sig, signer, name, ng_priv);
328 memset(ap_priv, 0, sizeof ap_priv);
329 ap_priv[10] = 1;
331 memset(ap_sig, 81, sizeof ap_sig); // temp
333 sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", ng_id);
334 sprintf(name, "AP%08x%08x", 1, 2);
335 make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv);
337 sha(ap_cert + 0x80, 0x100, hash);
338 generate_ecdsa(ap_sig, ap_sig + 30, ng_priv, hash);
339 make_ec_cert(ap_cert, ap_sig, signer, name, ap_priv);
341 data_size = files_size + 0x80;
343 data = malloc(data_size);
344 if (!data)
345 fatal("malloc");
346 fseek(fp, 0xf0c0, SEEK_SET);
347 if (fread(data, data_size, 1, fp) != 1)
348 fatal("read data for sig check");
349 sha(data, data_size, hash);
350 sha(hash, 20, hash);
351 free(data);
353 generate_ecdsa(sig, sig + 30, ap_priv, hash);
354 wbe32(sig + 60, 0x2f536969);
356 if (fwrite(sig, sizeof sig, 1, fp) != 1)
357 fatal("write sig");
358 if (fwrite(ng_cert, sizeof ng_cert, 1, fp) != 1)
359 fatal("write NG cert");
360 if (fwrite(ap_cert, sizeof ap_cert, 1, fp) != 1)
361 fatal("write AP cert");
364 int main(int argc, char **argv)
366 u64 title_id;
367 u8 tmp[4];
368 u32 i;
370 if (argc != 3) {
371 fprintf(stderr, "Usage: %s <srcdir> <data.bin>\n", argv[0]);
372 return 1;
375 get_key("sd-key", sd_key, 16);
376 get_key("sd-iv", sd_iv, 16);
377 get_key("md5-blanker", md5_blanker, 16);
379 get_key("default/NG-id", tmp, 4);
380 ng_id = be32(tmp);
381 get_key("default/NG-mac", ng_mac, 6);
382 get_key("default/NG-priv", ng_priv, 30);
383 get_key("default/NG-sig", ng_sig, 60);
385 if (sscanf(argv[1], "%016llx", &title_id) != 1)
386 ERROR("not a correct title id");
388 fp = fopen(argv[2], "wb+");
389 if (!fp)
390 fatal("open %s", argv[2]);
392 if (chdir(argv[1]))
393 fatal("chdir %s", argv[1]);
395 do_file_header(title_id);
397 find_files();
399 do_backup_header(title_id);
401 for (i = 0; i < n_files; i++)
402 do_file(i);
404 if (chdir(".."))
405 fatal("chdir ..");
407 do_sig();
409 fclose(fp);
411 return 0;