twintig: Be less verbose by default
[svpe-tools.git] / tachtig.c
blob7fab7e7d070801b45e1a8482157656b18b99c55a
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 <unistd.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <stdio.h>
12 #include "tools.h"
14 #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0)
16 static u8 sd_key[16];
17 static u8 sd_iv[16];
18 static u8 md5_blanker[16];
20 static FILE *fp;
21 static u32 n_files;
22 static u32 files_size;
23 static u32 total_size;
24 static u8 header[0xf0c0];
26 static void output_image(u8 *data, u32 w, u32 h, const char *name)
28 FILE *fp;
29 u32 x, y;
31 fp = fopen(name, "wb");
32 if (!fp)
33 fatal("open %s", name);
35 fprintf(fp, "P6 %d %d 255\n", w, h);
37 for (y = 0; y < h; y++)
38 for (x = 0; x < w; x++) {
39 u8 pix[3];
40 u16 raw;
41 u32 x0, x1, y0, y1, off;
43 x0 = x & 3;
44 x1 = x >> 2;
45 y0 = y & 3;
46 y1 = y >> 2;
47 off = x0 + 4 * y0 + 16 * x1 + 4 * w * y1;
49 raw = be16(data + 2*off);
51 // RGB5A3
52 if (raw & 0x8000) {
53 pix[0] = (raw >> 7) & 0xf8;
54 pix[1] = (raw >> 2) & 0xf8;
55 pix[2] = (raw << 3) & 0xf8;
56 } else {
57 pix[0] = (raw >> 4) & 0xf0;
58 pix[1] = raw & 0xf0;
59 pix[2] = (raw << 4) & 0xf0;
62 if (fwrite(pix, 3, 1, fp) != 1)
63 fatal("write %s", name);
66 fclose(fp);
69 static void do_file_header(void)
71 u8 md5_file[16];
72 u8 md5_calc[16];
73 u32 header_size;
74 char name[256];
75 char dir[17];
76 FILE *out;
77 u32 i;
79 if (fread(header, sizeof header, 1, fp) != 1)
80 fatal("read file header");
82 aes_cbc_dec(sd_key, sd_iv, header, sizeof header, header);
84 memcpy(md5_file, header + 0x0e, 16);
85 memcpy(header + 0x0e, md5_blanker, 16);
86 md5(header, sizeof header, md5_calc);
88 if (memcmp(md5_file, md5_calc, 0x10))
89 ERROR("MD5 mismatch");
91 header_size = be32(header + 8);
92 if (header_size < 0x72a0 || header_size > 0xf0a0
93 || (header_size - 0x60a0) % 0x1200 != 0)
94 ERROR("bad file header size");
96 snprintf(dir, sizeof dir, "%016llx", be64(header));
97 if (mkdir(dir, 0777))
98 fatal("mkdir %s", dir);
99 if (chdir(dir))
100 fatal("chdir %s", dir);
102 out = fopen("###title###", "wb");
103 if (!out)
104 fatal("open ###title###");
105 if (fwrite(header + 0x40, 0x80, 1, out) != 1)
106 fatal("write ###title###");
107 fclose(out);
109 output_image(header + 0xc0, 192, 64, "###banner###.ppm");
110 if (header_size == 0x72a0)
111 output_image(header + 0x60c0, 48, 48, "###icon###.ppm");
112 else
113 for (i = 0; 0x1200*i + 0x60c0 < header_size; i++) {
114 snprintf(name, sizeof name, "###icon%d###.ppm", i);
115 output_image(header + 0x60c0 + 0x1200*i, 48, 48, name);
119 static void do_backup_header(void)
121 u8 header[0x80];
123 if (fread(header, sizeof header, 1, fp) != 1)
124 fatal("read backup header");
126 if (be32(header + 4) != 0x426b0001)
127 ERROR("no Bk header");
128 if (be32(header) != 0x70)
129 ERROR("wrong Bk header size");
131 fprintf(stderr, "NG id: %08x\n", be32(header + 8));
133 n_files = be32(header + 0x0c);
134 files_size = be32(header + 0x10);
135 total_size = be32(header + 0x1c);
137 fprintf(stderr, "%d files\n", n_files);
140 static mode_t perm_to_mode(u8 perm)
142 mode_t mode;
143 u32 i;
145 mode = 0;
146 for (i = 0; i < 3; i++) {
147 mode <<= 3;
148 if (perm & 0x20)
149 mode |= 3;
150 if (perm & 0x10)
151 mode |= 5;
152 perm <<= 2;
155 return mode;
158 static void do_file(void)
160 u8 header[0x80];
161 u32 size;
162 u32 rounded_size;
163 u8 perm, attr, type;
164 char *name;
165 u8 *data;
166 FILE *out;
167 mode_t mode;
169 if (fread(header, sizeof header, 1, fp) != 1)
170 fatal("read file header");
172 if (be32(header) != 0x03adf17e)
173 ERROR("bad file header");
175 size = be32(header + 4);
176 perm = header[8];
177 attr = header[9];
178 type = header[10];
179 name = header + 11;
181 fprintf(stderr, "file: size=%08x perm=%02x attr=%02x type=%02x name=%s\n", size, perm, attr, type, name);
183 mode = perm_to_mode(perm);
185 switch (type) {
186 case 1:
187 rounded_size = (size + 63) & ~63;
188 data = malloc(rounded_size);
189 if (!data)
190 fatal("malloc");
191 if (fread(data, rounded_size, 1, fp) != 1)
192 fatal("read file data for %s", name);
194 aes_cbc_dec(sd_key, header + 0x50, data, rounded_size, data);
196 out = fopen(name, "wb");
197 if (!out)
198 fatal("open %s", name);
199 if (fwrite(data, size, 1, out) != 1)
200 fatal("write %s", name);
201 fclose(out);
203 mode &= ~0111;
205 free(data);
206 break;
208 case 2:
209 if (mkdir(name, 0777))
210 fatal("mkdir %s", name);
211 break;
213 default:
214 ERROR("unhandled file type");
217 if (chmod(name, mode))
218 fatal("chmod %s", name);
221 static void do_sig(void)
223 u8 sig[0x40];
224 u8 ng_cert[0x180];
225 u8 ap_cert[0x180];
226 u8 hash[0x14];
227 u8 *data;
228 u32 data_size;
229 int ok;
231 if (fread(sig, sizeof sig, 1, fp) != 1)
232 fatal("read signature");
233 if (fread(ng_cert, sizeof ng_cert, 1, fp) != 1)
234 fatal("read NG cert");
235 if (fread(ap_cert, sizeof ap_cert, 1, fp) != 1)
236 fatal("read AP cert");
238 data_size = total_size - 0x340;
240 data = malloc(data_size);
241 if (!data)
242 fatal("malloc");
243 fseek(fp, 0xf0c0, SEEK_SET);
244 if (fread(data, data_size, 1, fp) != 1)
245 fatal("read data for sig check");
246 sha(data, data_size, hash);
247 sha(hash, 20, hash);
248 free(data);
250 ok = check_ec(ng_cert, ap_cert, sig, hash);
251 printf("ok: %d\n", ok);
254 int main(int argc, char **argv)
256 u32 i;
257 u32 mode;
259 if (argc != 2) {
260 fprintf(stderr, "Usage: %s <data.bin>\n", argv[0]);
261 return 1;
264 get_key("sd-key", sd_key, 16);
265 get_key("sd-iv", sd_iv, 16);
266 get_key("md5-blanker", md5_blanker, 16);
268 fp = fopen(argv[1], "rb");
269 if (!fp)
270 fatal("open %s", argv[1]);
272 do_file_header();
273 do_backup_header();
275 for (i = 0; i < n_files; i++)
276 do_file();
278 mode = perm_to_mode(header[0x0c]);
279 if (chmod(".", mode))
280 fatal("chmod .");
281 if (chdir(".."))
282 fatal("chdir ..");
284 do_sig();
286 fclose(fp);
288 return 0;