Remove extra newline in hexdump output
[svpe-tools.git] / tools.c
blob9acd6a9b507700fa896f258a3fa1492a68aaae4f
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 "tools.h"
7 #include <openssl/md5.h>
8 #include <openssl/aes.h>
9 #include <openssl/sha.h>
10 #include <stdarg.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
16 // basic data types
19 u16 be16(u8 *p)
21 return (p[0] << 8) | p[1];
24 u32 be32(u8 *p)
26 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
29 u64 be64(u8 *p)
31 return ((u64)be32(p) << 32) | be32(p + 4);
34 u64 be34(u8 *p)
36 return 4 * (u64)be32(p);
40 // crypto
43 void md5(u8 *data, u32 len, u8 *hash)
45 MD5(data, len, hash);
48 void sha(u8 *data, u32 len, u8 *hash)
50 SHA1(data, len, hash);
53 void get_key(const char *name, u8 *key, u32 len)
55 char path[256];
56 char *home;
57 FILE *fp;
59 home = getenv("HOME");
60 if (home == 0)
61 fatal("cannot find HOME");
62 snprintf(path, sizeof path, "%s/.wii/%s", home, name);
64 fp = fopen(path, "rb");
65 if (fp == 0)
66 fatal("cannot open %s", name);
67 if (fread(key, len, 1, fp) != 1)
68 fatal("error reading %s", name);
69 fclose(fp);
72 void aes_cbc_dec(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out)
74 AES_KEY aes_key;
76 AES_set_decrypt_key(key, 128, &aes_key);
77 AES_cbc_encrypt(in, out, len, &aes_key, iv, AES_DECRYPT);
80 void decrypt_title_key(u8 *title_key_crypted, u8 *title_id, u8 *title_key)
82 u8 common_key[16];
83 u8 iv[16];
85 get_key("common-key", common_key, 16);
87 memset(iv, 0, sizeof iv);
88 memcpy(iv, title_id, 8);
89 aes_cbc_dec(common_key, iv, title_key_crypted, 16, title_key);
92 static u8 root_key[0x204];
93 static u8 *get_root_key(void)
95 get_key("root-key", root_key, sizeof root_key);
96 return root_key;
99 static u32 get_sig_len(u8 *sig)
101 u32 type;
103 type = be32(sig);
104 switch (type - 0x10000) {
105 case 0:
106 return 0x240;
108 case 1:
109 return 0x140;
111 case 2:
112 return 0x80;
115 fprintf(stderr, "get_sig_len(): unhandled sig type %08x\n", type);
116 return 0;
119 static u32 get_sub_len(u8 *sub)
121 u32 type;
123 type = be32(sub + 0x40);
124 switch (type) {
125 case 0:
126 return 0x2c0;
128 case 1:
129 return 0x1c0;
131 case 2:
132 return 0x100;
135 fprintf(stderr, "get_sub_len(): unhandled sub type %08x\n", type);
136 return 0;
139 int check_ec(u8 *ng, u8 *ap, u8 *sig, u8 *sig_hash)
141 u8 ap_hash[20];
142 u8 *ng_Q, *ap_R, *ap_S;
143 u8 *ap_Q, *sig_R, *sig_S;
145 ng_Q = ng + 0x0108;
146 ap_R = ap + 0x04;
147 ap_S = ap + 0x22;
149 SHA1(ap + 0x80, 0x100, ap_hash);
151 ap_Q = ap + 0x0108;
152 sig_R = sig;
153 sig_S = sig + 30;
155 return check_ecdsa(ng_Q, ap_R, ap_S, ap_hash)
156 && check_ecdsa(ap_Q, sig_R, sig_S, sig_hash);
159 static int check_rsa(u8 *h, u8 *sig, u8 *key, u32 n)
161 u8 correct[0x200];
162 u8 x[0x200];
163 static const u8 ber[16] = "\x00\x30\x21\x30\x09\x06\x05\x2b"
164 "\x0e\x03\x02\x1a\x05\x00\x04\x14";
166 correct[0] = 0;
167 correct[1] = 1;
168 memset(correct + 2, 0xff, n - 38);
169 memcpy(correct + n - 36, ber, 16);
170 memcpy(correct + n - 20, h, 20);
171 //fprintf(stderr, "correct is:\n");
172 //hexdump(correct, n);
174 bn_exp(x, sig, key, n, key + n, 4);
175 //fprintf(stderr, "x is:\n");
176 //hexdump(x, n);
178 if (memcmp(correct, x, n) == 0)
179 return 0;
181 return -5;
184 static int check_hash(u8 *h, u8 *sig, u8 *key)
186 u32 type;
188 type = be32(sig) - 0x10000;
189 if (type != be32(key + 0x40))
190 return -6;
192 switch (type) {
193 case 1:
194 return check_rsa(h, sig + 4, key + 0x88, 0x100);
197 return -7;
200 static u8 *find_cert_in_chain(u8 *sub, u8 *cert, u32 cert_len)
202 char parent[64];
203 char *child;
204 u32 sig_len, sub_len;
205 u8 *p;
206 u8 *issuer;
208 strncpy(parent, sub, sizeof parent);
209 parent[sizeof parent - 1] = 0;
210 child = strrchr(parent, '-');
211 if (child)
212 *child++ = 0;
213 else {
214 *parent = 0;
215 child = sub;
218 for (p = cert; p < cert + cert_len; p += sig_len + sub_len) {
219 sig_len = get_sig_len(p);
220 if (sig_len == 0)
221 return 0;
222 issuer = p + sig_len;
223 sub_len = get_sub_len(issuer);
224 if (sub_len == 0)
225 return 0;
227 if (strcmp(parent, issuer) == 0
228 && strcmp(child, issuer + 0x44) == 0)
229 return p;
232 return 0;
235 int check_cert_chain(u8 *data, u32 data_len, u8 *cert, u32 cert_len)
237 u8 *sig;
238 u8 *sub;
239 u32 sig_len;
240 u32 sub_len;
241 u8 h[20];
242 u8 *key_cert;
243 u8 *key;
244 int ret;
246 sig = data;
247 sig_len = get_sig_len(sig);
248 if (sig_len == 0)
249 return -1;
250 sub = data + sig_len;
251 sub_len = data_len - sig_len;
252 if (sub_len == 0)
253 return -2;
255 for (;;) {
256 fprintf(stderr, ">>>>>> checking sig by %s...\n", sub);
257 if (strcmp(sub, "Root") == 0) {
258 key = get_root_key();
259 sha(sub, sub_len, h);
260 if (be32(sig) != 0x10000)
261 return -8;
262 return check_rsa(h, sig + 4, key, 0x200);
265 key_cert = find_cert_in_chain(sub, cert, cert_len);
266 if (key_cert == 0)
267 return -3;
269 key = key_cert + get_sig_len(key_cert);
271 sha(sub, sub_len, h);
272 ret = check_hash(h, sig, key);
273 if (ret)
274 return ret;
276 sig = key_cert;
277 sig_len = get_sig_len(sig);
278 if (sig_len == 0)
279 return -4;
280 sub = sig + sig_len;
281 sub_len = get_sub_len(sub);
282 if (sub_len == 0)
283 return -5;
288 // compression
291 void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size)
293 u32 nout;
294 u8 bits;
295 u32 nbits;
296 u32 n, d, i;
298 nbits = 0;
299 in += 0x10;
300 for (nout = 0; nout < out_size; ) {
301 if (nbits == 0) {
302 bits = *in++;
303 nbits = 8;
306 if ((bits & 0x80) != 0) {
307 *out++ = *in++;
308 nout++;
309 } else {
310 n = *in++;
311 d = *in++;
312 d |= (n << 8) & 0xf00;
313 n >>= 4;
314 if (n == 0)
315 n = 0x10 + *in++;
316 n += 2;
317 d++;
319 for (i = 0; i < n; i++) {
320 *out = *(out - d);
321 out++;
323 nout += n;
326 nbits--;
327 bits <<= 1;
332 // error handling
335 void fatal(const char *s, ...)
337 char message[256];
338 va_list ap;
340 va_start(ap, s);
341 vsnprintf(message, sizeof message, s, ap);
343 perror(message);
345 exit(1);
349 // output formatting
352 void print_bytes(u8 *x, u32 n)
354 u32 i;
356 for (i = 0; i < n; i++)
357 fprintf(stderr, "%02x", x[i]);
360 void hexdump(u8 *x, u32 n)
362 u32 i, j;
364 for (i = 0; i < n; i += 16) {
365 fprintf(stderr, "%04x:", i);
366 for (j = 0; j < 16 && i + j < n; j++) {
367 if ((j & 3) == 0)
368 fprintf(stderr, " ");
369 fprintf(stderr, "%02x", *x++);
371 fprintf(stderr, "\n");
375 void dump_tmd(u8 *tmd)
377 u32 i, n;
378 u8 *p;
380 printf(" issuer: %s\n", tmd + 0x140);
381 printf(" sys_version: %016llx\n", be64(tmd + 0x0184));
382 printf(" title_id: %016llx\n", be64(tmd + 0x018c));
383 printf(" title_type: %08x\n", be32(tmd + 0x0194));
384 printf(" group_id: %04x\n", be16(tmd + 0x0198));
385 printf("title_version: %04x\n", be16(tmd + 0x01dc));
386 printf(" num_contents: %04x\n", be16(tmd + 0x01de));
387 printf(" boot_index: %04x\n", be16(tmd + 0x01e0));
389 n = be16(tmd + 0x01de);
390 p = tmd + 0x01e4;
391 for (i = 0; i < n; i++) {
392 printf("cid %08x index %04x type %04x size %08llx\n",
393 be32(p), be16(p + 4), be16(p + 6), be64(p + 8));
394 p += 0x24;