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
17 #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0)
21 static u8 md5_blanker
[16];
25 static u8 ng_priv
[30];
30 static u8 header
[0xf0c0];
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
)
43 fp
= fopen(name
, "rb");
45 fatal("open %s", name
);
47 if (fscanf(fp
, "P6 %d %d 255\n", &ww
, &hh
) != 2)
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
++) {
56 u32 x0
, x1
, y0
, y1
, off
;
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;
72 wbe16(data
+ 2*off
, raw
);
78 static u8
perm_from_path(const char *path
)
86 fatal("stat %s", path
);
90 for (i
= 0; i
< 3; i
++) {
102 static void do_file_header(u64 title_id
)
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");
119 fatal("open ###title###");
120 if (fread(header
+ 0x40, 0x80, 1, in
) != 1)
121 fatal("read ###title###");
124 read_image(header
+ 0xc0, 192, 64, "###banner###.ppm");
126 in
= fopen("###icon###.ppm", "rb");
129 wbe32(header
+ 8, 0x72a0);
130 read_image(header
+ 0x60c0, 48, 48, "###icon###.ppm");
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
)
159 dir
= opendir(path
? path
: ".");
161 fatal("opendir %s", path
? path
: ".");
163 while ((de
= readdir(dir
))) {
164 if (strcmp(de
->d_name
, ".") == 0)
166 if (strcmp(de
->d_name
, "..") == 0)
168 if (strncmp(de
->d_name
, "###", 3) == 0)
172 len
= snprintf(name
, sizeof name
, "%s", de
->d_name
);
174 len
= snprintf(name
, sizeof name
, "%s/%s", path
,
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
);
189 fatal("stat %s", name
);
193 p
= files
[n_files
++];
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
);
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)
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
)
233 memset(header
, 0, sizeof header
);
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
)
259 header
= files
[file_no
];
261 size
= be32(header
+ 4);
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
);
275 rounded_size
= round_up(size
, 0x40);
277 data
= malloc(rounded_size
);
279 fatal("malloc data");
281 in
= fopen(name
, "rb");
283 fatal("open %s", name
);
284 if (fread(data
, size
, 1, in
) != 1)
285 fatal("read %s", name
);
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
);
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)
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
);
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
);
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
);
353 generate_ecdsa(sig
, sig
+ 30, ap_priv
, hash
);
354 wbe32(sig
+ 60, 0x2f536969);
356 if (fwrite(sig
, sizeof sig
, 1, fp
) != 1)
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
)
371 fprintf(stderr
, "Usage: %s <srcdir> <data.bin>\n", argv
[0]);
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);
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+");
390 fatal("open %s", argv
[2]);
393 fatal("chdir %s", argv
[1]);
395 do_file_header(title_id
);
399 do_backup_header(title_id
);
401 for (i
= 0; i
< n_files
; i
++)